Merge branch 'master' of http://github.com/jboner/akka
|
|
@ -78,7 +78,7 @@ object Chameneos {
|
|||
var sumMeetings = 0
|
||||
var numFaded = 0
|
||||
|
||||
override def preStart = {
|
||||
override def preStart() = {
|
||||
for (i <- 0 until numChameneos) actorOf(new Chameneo(self, colours(i % 3), i))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class RestartStrategySpec extends JUnitSuite {
|
|||
secondRestartLatch.open
|
||||
}
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
}
|
||||
})
|
||||
|
|
@ -131,7 +131,7 @@ class RestartStrategySpec extends JUnitSuite {
|
|||
thirdRestartLatch.open
|
||||
}
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
if (restartLatch.isOpen) {
|
||||
secondRestartLatch.open
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ class RestartStrategySpec extends JUnitSuite {
|
|||
secondRestartLatch.open
|
||||
}
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
}
|
||||
})
|
||||
|
|
@ -243,7 +243,7 @@ class RestartStrategySpec extends JUnitSuite {
|
|||
restartLatch.open
|
||||
}
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach
|
|||
|
||||
inits.get must be (3)
|
||||
|
||||
supervisor.shutdown
|
||||
supervisor.shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
class Ticket669Spec extends WordSpec with MustMatchers with BeforeAndAfterAll {
|
||||
import Ticket669Spec._
|
||||
|
||||
override def afterAll = Actor.registry.shutdownAll()
|
||||
override def afterAll() { Actor.registry.shutdownAll() }
|
||||
|
||||
"A supervised actor with lifecycle PERMANENT" should {
|
||||
"be able to reply on failure during preRestart" in {
|
||||
|
|
@ -65,7 +65,7 @@ object Ticket669Spec {
|
|||
self.reply_?("failure1")
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
self.reply_?("failure2")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
|||
|
||||
latch.await(10,TimeUnit.SECONDS) should equal (true)
|
||||
result.get should equal (42)
|
||||
List(x,y,z).foreach(_.shutdown)
|
||||
List(x,y,z).foreach(_.shutdown())
|
||||
}
|
||||
|
||||
it("should be able to sum a sequence of ints") {
|
||||
|
|
@ -67,7 +67,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
|||
|
||||
latch.await(10,TimeUnit.SECONDS) should equal (true)
|
||||
result.get should equal (sum(0,ints(0,1000)))
|
||||
List(x,y,z).foreach(_.shutdown)
|
||||
List(x,y,z).foreach(_.shutdown())
|
||||
}
|
||||
/*
|
||||
it("should be able to join streams") {
|
||||
|
|
@ -158,7 +158,7 @@ class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
|||
val setV = thread {
|
||||
v << y
|
||||
}
|
||||
List(x,y,z,v) foreach (_.shutdown)
|
||||
List(x,y,z,v) foreach (_.shutdown())
|
||||
latch.await(2,TimeUnit.SECONDS) should equal (true)
|
||||
}*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ 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
|
||||
|
||||
object ActorModelSpec {
|
||||
|
||||
|
|
@ -110,13 +111,13 @@ object ActorModelSpec {
|
|||
super.dispatch(invocation)
|
||||
}
|
||||
|
||||
private[akka] abstract override def start {
|
||||
super.start
|
||||
private[akka] abstract override def start() {
|
||||
super.start()
|
||||
starts.incrementAndGet()
|
||||
}
|
||||
|
||||
private[akka] abstract override def shutdown {
|
||||
super.shutdown
|
||||
private[akka] abstract override def shutdown() {
|
||||
super.shutdown()
|
||||
stops.incrementAndGet()
|
||||
}
|
||||
}
|
||||
|
|
@ -216,6 +217,21 @@ abstract class ActorModelSpec extends JUnitSuite {
|
|||
msgsProcessed = 0,
|
||||
restarts = 0
|
||||
)
|
||||
|
||||
val futures = for(i <- 1 to 10) yield Future { i }
|
||||
await(dispatcher.stops.get == 2)(withinMs = dispatcher.timeoutMs * 5)
|
||||
assertDispatcher(dispatcher)(starts = 2, stops = 2)
|
||||
|
||||
val a2 = newTestActor
|
||||
a2.start
|
||||
val futures2 = for(i <- 1 to 10) yield Future { i }
|
||||
|
||||
await(dispatcher.starts.get == 3)(withinMs = dispatcher.timeoutMs * 5)
|
||||
assertDispatcher(dispatcher)(starts = 3, stops = 2)
|
||||
|
||||
a2.stop
|
||||
await(dispatcher.stops.get == 3)(withinMs = dispatcher.timeoutMs * 5)
|
||||
assertDispatcher(dispatcher)(starts = 3, stops = 3)
|
||||
}
|
||||
|
||||
@Test def dispatcherShouldProcessMessagesOneAtATime {
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ class FutureSpec extends JUnitSuite {
|
|||
val future1 = actor1 !!! "Hello" flatMap ((s: String) => actor2 !!! s)
|
||||
val future2 = actor1 !!! "Hello" flatMap (actor2 !!! (_: String))
|
||||
val future3 = actor1 !!! "Hello" flatMap (actor2 !!! (_: Int))
|
||||
assert(Some(Right("WORLD")) === future1.await.value)
|
||||
assert(Some(Right("WORLD")) === future2.await.value)
|
||||
intercept[ClassCastException] { future3.await.resultOrException }
|
||||
assert((future1.get: Any) === "WORLD")
|
||||
assert((future2.get: Any) === "WORLD")
|
||||
intercept[ClassCastException] { future3.get }
|
||||
actor1.stop()
|
||||
actor2.stop()
|
||||
}
|
||||
|
|
@ -74,8 +74,8 @@ class FutureSpec extends JUnitSuite {
|
|||
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 !!! _)
|
||||
assert(Some(Right("WORLD")) === future1.await.value)
|
||||
intercept[MatchError] { future2.await.resultOrException }
|
||||
assert((future1.get: Any) === "WORLD")
|
||||
intercept[MatchError] { future2.get }
|
||||
actor1.stop()
|
||||
actor2.stop()
|
||||
}
|
||||
|
|
@ -102,8 +102,8 @@ class FutureSpec extends JUnitSuite {
|
|||
c: String <- actor !!! 7
|
||||
} yield b + "-" + c
|
||||
|
||||
assert(Some(Right("10-14")) === future1.await.value)
|
||||
intercept[ClassCastException] { future2.await.resultOrException }
|
||||
assert(future1.get === "10-14")
|
||||
intercept[ClassCastException] { future2.get }
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
|
|
@ -118,19 +118,64 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
|
||||
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 }
|
||||
Res(a: Int) <- actor !!! Req("Hello")
|
||||
Res(b: String) <- actor !!! Req(a)
|
||||
Res(c: String) <- actor !!! Req(7)
|
||||
} yield b + "-" + c
|
||||
|
||||
val future2 = for {
|
||||
a <- actor !!! Req("Hello") collect { case Res(x: Int) => x }
|
||||
b <- actor !!! Req(a) collect { case Res(x: Int) => x }
|
||||
c <- actor !!! Req(7) collect { case Res(x: String) => x }
|
||||
Res(a: Int) <- actor !!! Req("Hello")
|
||||
Res(b: Int) <- actor !!! Req(a)
|
||||
Res(c: Int) <- actor !!! Req(7)
|
||||
} yield b + "-" + c
|
||||
|
||||
assert(Some(Right("10-14")) === future1.await.value)
|
||||
intercept[MatchError] { future2.await.resultOrException }
|
||||
assert(future1.get === "10-14")
|
||||
intercept[MatchError] { future2.get }
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
@Test def shouldMapMatchedExceptionsToResult {
|
||||
val future1 = Future(5)
|
||||
val future2 = future1 map (_ / 0)
|
||||
val future3 = future2 map (_.toString)
|
||||
|
||||
val future4 = future1 failure {
|
||||
case e: ArithmeticException => 0
|
||||
} map (_.toString)
|
||||
|
||||
val future5 = future2 failure {
|
||||
case e: ArithmeticException => 0
|
||||
} map (_.toString)
|
||||
|
||||
val future6 = future2 failure {
|
||||
case e: MatchError => 0
|
||||
} map (_.toString)
|
||||
|
||||
val future7 = future3 failure { case e: ArithmeticException => "You got ERROR" }
|
||||
|
||||
val actor = actorOf[TestActor].start()
|
||||
|
||||
val future8 = actor !!! "Failure"
|
||||
val future9 = actor !!! "Failure" failure {
|
||||
case e: RuntimeException => "FAIL!"
|
||||
}
|
||||
val future10 = actor !!! "Hello" failure {
|
||||
case e: RuntimeException => "FAIL!"
|
||||
}
|
||||
val future11 = actor !!! "Failure" failure { case _ => "Oops!" }
|
||||
|
||||
assert(future1.get === 5)
|
||||
intercept[ArithmeticException] { future2.get }
|
||||
intercept[ArithmeticException] { future3.get }
|
||||
assert(future4.get === "5")
|
||||
assert(future5.get === "0")
|
||||
intercept[ArithmeticException] { future6.get }
|
||||
assert(future7.get === "You got ERROR")
|
||||
intercept[RuntimeException] { future8.get }
|
||||
assert(future9.get === "FAIL!")
|
||||
assert(future10.get === "World")
|
||||
assert(future11.get === "Oops!")
|
||||
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
|
|
@ -215,8 +260,9 @@ 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.!!) }
|
||||
assert(Futures.fold(0)(futures)(_ + _).awaitBlocking.result.get === 45)
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!, timeout) }
|
||||
assert(Futures.fold(0, timeout)(futures)(_ + _).await.result.get === 45)
|
||||
}
|
||||
|
||||
@Test def shouldFoldResultsByComposing {
|
||||
|
|
@ -225,8 +271,8 @@ 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.!!) }
|
||||
assert(futures.foldLeft(Future(0))((fr, fa) => for (r <- fr; a <- fa) yield (r + a)).awaitBlocking.result.get === 45)
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!, 10000) }
|
||||
assert(futures.foldLeft(Future(0))((fr, fa) => for (r <- fr; a <- fa) yield (r + a)).get === 45)
|
||||
}
|
||||
|
||||
@Test def shouldFoldResultsWithException {
|
||||
|
|
@ -240,12 +286,13 @@ class FutureSpec extends JUnitSuite {
|
|||
}
|
||||
}).start()
|
||||
}
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!) }
|
||||
assert(Futures.fold(0)(futures)(_ + _).awaitBlocking.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!, timeout) }
|
||||
assert(Futures.fold(0, timeout)(futures)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
}
|
||||
|
||||
@Test def shouldFoldReturnZeroOnEmptyInput {
|
||||
assert(Futures.fold(0)(List[Future[Int]]())(_ + _).awaitBlocking.result.get === 0)
|
||||
assert(Futures.fold(0)(List[Future[Int]]())(_ + _).get === 0)
|
||||
}
|
||||
|
||||
@Test def shouldReduceResults {
|
||||
|
|
@ -254,8 +301,9 @@ 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.!!) }
|
||||
assert(Futures.reduce(futures)(_ + _).awaitBlocking.result.get === 45)
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!, timeout) }
|
||||
assert(Futures.reduce(futures, timeout)(_ + _).get === 45)
|
||||
}
|
||||
|
||||
@Test def shouldReduceResultsWithException {
|
||||
|
|
@ -269,34 +317,15 @@ class FutureSpec extends JUnitSuite {
|
|||
}
|
||||
}).start()
|
||||
}
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!) }
|
||||
assert(Futures.reduce(futures)(_ + _).awaitBlocking.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!, timeout) }
|
||||
assert(Futures.reduce(futures, timeout)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
}
|
||||
|
||||
@Test(expected = classOf[UnsupportedOperationException]) def shouldReduceThrowIAEOnEmptyInput {
|
||||
Futures.reduce(List[Future[Int]]())(_ + _).await.resultOrException
|
||||
}
|
||||
|
||||
@Test def resultWithinShouldNotThrowExceptions {
|
||||
val latch = new StandardLatch
|
||||
|
||||
val actors = (1 to 10).toList map { _ =>
|
||||
actorOf(new Actor {
|
||||
def receive = { case (add: Int, wait: Boolean, latch: StandardLatch) => if (wait) latch.await; self reply_? add }
|
||||
}).start()
|
||||
}
|
||||
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) => actor.!!) }
|
||||
val result = for(f <- futures) yield f.valueWithin(2, TimeUnit.SECONDS)
|
||||
latch.open
|
||||
val done = result collect { case Some(Right(x)) => x }
|
||||
val undone = result collect { case None => None }
|
||||
val errors = result collect { case Some(Left(t)) => t }
|
||||
assert(done.size === 5)
|
||||
assert(undone.size === 5)
|
||||
assert(errors.size === 0)
|
||||
}
|
||||
|
||||
@Test def receiveShouldExecuteOnComplete {
|
||||
val latch = new StandardLatch
|
||||
val actor = actorOf[TestActor].start()
|
||||
|
|
@ -344,41 +373,289 @@ class FutureSpec extends JUnitSuite {
|
|||
assert(f3.resultOrException === Some("SUCCESS"))
|
||||
|
||||
// make sure all futures are completed in dispatcher
|
||||
assert(Dispatchers.defaultGlobalDispatcher.futureQueueSize === 0)
|
||||
assert(Dispatchers.defaultGlobalDispatcher.pendingFutures === 0)
|
||||
}
|
||||
|
||||
@Test def shouldBlockUntilResult {
|
||||
val latch = new StandardLatch
|
||||
|
||||
val f = Future({ latch.await; 5})
|
||||
val f2 = Future({ f() + 5 })
|
||||
val f2 = Future({ f.get + 5 })
|
||||
|
||||
assert(f2.resultOrException === None)
|
||||
latch.open
|
||||
assert(f2() === 10)
|
||||
assert(f2.get === 10)
|
||||
|
||||
val f3 = Future({ Thread.sleep(100); 5}, 10)
|
||||
intercept[FutureTimeoutException] {
|
||||
f3()
|
||||
f3.get
|
||||
}
|
||||
}
|
||||
|
||||
@Test def lesslessIsMore {
|
||||
import akka.actor.Actor.spawn
|
||||
val dataflowVar, dataflowVar2 = new DefaultCompletableFuture[Int](Long.MaxValue)
|
||||
val begin, end = new StandardLatch
|
||||
spawn {
|
||||
begin.await
|
||||
dataflowVar2 << dataflowVar
|
||||
end.open
|
||||
@Test def futureComposingWithContinuations {
|
||||
import Future.flow
|
||||
|
||||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x flatMap (actor !!! _)
|
||||
|
||||
val r = flow(x() + " " + y[String]() + "!")
|
||||
|
||||
assert(r.get === "Hello World!")
|
||||
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def futureComposingWithContinuationsFailureDivideZero {
|
||||
import Future.flow
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x map (_.length)
|
||||
|
||||
val r = flow(x() + " " + y.map(_ / 0).map(_.toString)(), 100)
|
||||
|
||||
intercept[java.lang.ArithmeticException](r.get)
|
||||
}
|
||||
|
||||
@Test def futureComposingWithContinuationsFailureCastInt {
|
||||
import Future.flow
|
||||
|
||||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future(3)
|
||||
val y = actor !!! "Hello"
|
||||
|
||||
val r = flow(x() + y[Int](), 100)
|
||||
|
||||
intercept[ClassCastException](r.get)
|
||||
}
|
||||
|
||||
@Test def futureComposingWithContinuationsFailureCastNothing {
|
||||
import Future.flow
|
||||
|
||||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = actor !!! "Hello"
|
||||
|
||||
val r = flow(x() + y())
|
||||
|
||||
intercept[ClassCastException](r.get)
|
||||
}
|
||||
|
||||
@Test def futureCompletingWithContinuations {
|
||||
import Future.flow
|
||||
|
||||
val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT)
|
||||
val ly, lz = new StandardLatch
|
||||
|
||||
val result = flow {
|
||||
y completeWith x
|
||||
ly.open // not within continuation
|
||||
|
||||
z << x
|
||||
lz.open // within continuation, will wait for 'z' to complete
|
||||
z() + y()
|
||||
}
|
||||
|
||||
spawn {
|
||||
dataflowVar << 5
|
||||
assert(ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
x << 5
|
||||
|
||||
assert(y.get === 5)
|
||||
assert(z.get === 5)
|
||||
assert(lz.isOpen)
|
||||
assert(result.get === 10)
|
||||
|
||||
val a, b, c = new DefaultCompletableFuture[Int](Actor.TIMEOUT)
|
||||
|
||||
val result2 = flow {
|
||||
val n = (a << c).result.get + 10
|
||||
b << (c() - 2)
|
||||
a() + n * b()
|
||||
}
|
||||
begin.open
|
||||
end.await
|
||||
assert(dataflowVar2() === 5)
|
||||
assert(dataflowVar.get === 5)
|
||||
|
||||
c completeWith Future(5)
|
||||
|
||||
assert(a.get === 5)
|
||||
assert(b.get === 3)
|
||||
assert(result2.get === 50)
|
||||
Thread.sleep(100)
|
||||
|
||||
// make sure all futures are completed in dispatcher
|
||||
assert(Dispatchers.defaultGlobalDispatcher.pendingFutures === 0)
|
||||
}
|
||||
|
||||
@Test def shouldNotAddOrRunCallbacksAfterFailureToBeCompletedBeforeExpiry {
|
||||
val latch = new StandardLatch
|
||||
val f = new DefaultCompletableFuture[Int](0)
|
||||
Thread.sleep(25)
|
||||
f.onComplete( _ => latch.open ) //Shouldn't throw any exception here
|
||||
|
||||
assert(f.isExpired) //Should be expired
|
||||
|
||||
f.complete(Right(1)) //Shouldn't complete the Future since it is expired
|
||||
|
||||
assert(f.value.isEmpty) //Shouldn't be completed
|
||||
assert(!latch.isOpen) //Shouldn't run the listener
|
||||
}
|
||||
|
||||
@Test def futureDataFlowShouldEmulateBlocking1 {
|
||||
import Future.flow
|
||||
|
||||
val one, two = new DefaultCompletableFuture[Int](1000 * 60)
|
||||
val simpleResult = flow {
|
||||
one() + two()
|
||||
}
|
||||
|
||||
assert(List(one, two, simpleResult).forall(_.isCompleted == false))
|
||||
|
||||
one << 1
|
||||
|
||||
assert(one.isCompleted)
|
||||
assert(List(two, simpleResult).forall(_.isCompleted == false))
|
||||
|
||||
two << 9
|
||||
|
||||
assert(List(one, two).forall(_.isCompleted == true))
|
||||
assert(simpleResult.get === 10)
|
||||
|
||||
}
|
||||
|
||||
@Test def futureDataFlowShouldEmulateBlocking2 {
|
||||
import Future.flow
|
||||
val x1, x2, y1, y2 = new DefaultCompletableFuture[Int](1000 * 60)
|
||||
val lx, ly, lz = new StandardLatch
|
||||
val result = flow {
|
||||
lx.open()
|
||||
x1 << y1
|
||||
ly.open()
|
||||
x2 << y2
|
||||
lz.open()
|
||||
x1() + x2()
|
||||
}
|
||||
assert(lx.isOpen)
|
||||
assert(!ly.isOpen)
|
||||
assert(!lz.isOpen)
|
||||
assert(List(x1,x2,y1,y2).forall(_.isCompleted == false))
|
||||
|
||||
y1 << 1 // When this is set, it should cascade down the line
|
||||
|
||||
assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(x1.get === 1)
|
||||
assert(!lz.isOpen)
|
||||
|
||||
y2 << 9 // When this is set, it should cascade down the line
|
||||
|
||||
assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(x2.get === 9)
|
||||
|
||||
assert(List(x1,x2,y1,y2).forall(_.isCompleted == true))
|
||||
|
||||
assert(result.get === 10)
|
||||
}
|
||||
|
||||
@Test def dataFlowAPIshouldbeSlick {
|
||||
import Future.flow
|
||||
|
||||
val i1, i2, s1, s2 = new StandardLatch
|
||||
|
||||
val callService1 = Future { i1.open; s1.awaitUninterruptible; 1 }
|
||||
val callService2 = Future { i2.open; s2.awaitUninterruptible; 9 }
|
||||
|
||||
val result = flow { callService1() + callService2() }
|
||||
|
||||
assert(!s1.isOpen)
|
||||
assert(!s2.isOpen)
|
||||
assert(!result.isCompleted)
|
||||
assert(i1.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(i2.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
s1.open
|
||||
s2.open
|
||||
assert(result.get === 10)
|
||||
}
|
||||
|
||||
@Test def futureCompletingWithContinuationsFailure {
|
||||
import Future.flow
|
||||
|
||||
val x, y, z = new DefaultCompletableFuture[Int](Actor.TIMEOUT)
|
||||
val ly, lz = new StandardLatch
|
||||
|
||||
val result = flow {
|
||||
y << x
|
||||
ly.open
|
||||
val oops = 1 / 0
|
||||
z << x
|
||||
lz.open
|
||||
z() + y() + oops
|
||||
}
|
||||
|
||||
assert(!ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
|
||||
x << 5
|
||||
|
||||
assert(y.get === 5)
|
||||
intercept[java.lang.ArithmeticException](result.get)
|
||||
assert(z.value === None)
|
||||
assert(!lz.isOpen)
|
||||
}
|
||||
|
||||
@Test def futureContinuationsShouldNotBlock {
|
||||
import Future.flow
|
||||
|
||||
val latch = new StandardLatch
|
||||
val future = Future {
|
||||
latch.await
|
||||
"Hello"
|
||||
}
|
||||
|
||||
val result = flow {
|
||||
Some(future()).filter(_ == "Hello")
|
||||
}
|
||||
|
||||
assert(!result.isCompleted)
|
||||
|
||||
latch.open
|
||||
|
||||
assert(result.get === Some("Hello"))
|
||||
}
|
||||
|
||||
@Test def futureFlowShouldBeTypeSafe {
|
||||
import Future.flow
|
||||
|
||||
def checkType[A: Manifest, B](in: Future[A], refmanifest: Manifest[B]): Boolean = manifest[A] == refmanifest
|
||||
|
||||
val rString = flow {
|
||||
val x = Future(5)
|
||||
x().toString
|
||||
}
|
||||
|
||||
val rInt = flow {
|
||||
val x = rString.apply
|
||||
val y = Future(5)
|
||||
x.length + y()
|
||||
}
|
||||
|
||||
assert(checkType(rString, manifest[String]))
|
||||
assert(checkType(rInt, manifest[Int]))
|
||||
assert(!checkType(rInt, manifest[String]))
|
||||
assert(!checkType(rInt, manifest[Nothing]))
|
||||
assert(!checkType(rInt, manifest[Any]))
|
||||
|
||||
rString.await
|
||||
rInt.await
|
||||
}
|
||||
|
||||
@Test def ticket812FutureDispatchCleanup {
|
||||
val dispatcher = implicitly[MessageDispatcher]
|
||||
assert(dispatcher.pendingFutures === 0)
|
||||
val future = Future({Thread.sleep(100);"Done"}, 10)
|
||||
intercept[FutureTimeoutException] { future.await }
|
||||
assert(dispatcher.pendingFutures === 1)
|
||||
Thread.sleep(100)
|
||||
assert(dispatcher.pendingFutures === 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ abstract class MailboxSpec extends
|
|||
def factory: MailboxType => MessageQueue
|
||||
|
||||
name should {
|
||||
"create a !blockDequeue && unbounded mailbox" in {
|
||||
val config = UnboundedMailbox(false)
|
||||
"create an unbounded mailbox" in {
|
||||
val config = UnboundedMailbox()
|
||||
val q = factory(config)
|
||||
ensureInitialMailboxState(config, q)
|
||||
|
||||
|
|
@ -37,8 +37,8 @@ abstract class MailboxSpec extends
|
|||
f.await.resultOrException must be === Some(null)
|
||||
}
|
||||
|
||||
"create a !blockDequeue and bounded mailbox with 10 capacity and with push timeout" in {
|
||||
val config = BoundedMailbox(false, 10, Duration(10,TimeUnit.MILLISECONDS))
|
||||
"create a bounded mailbox with 10 capacity and with push timeout" in {
|
||||
val config = BoundedMailbox(10, Duration(10,TimeUnit.MILLISECONDS))
|
||||
val q = factory(config)
|
||||
ensureInitialMailboxState(config, q)
|
||||
|
||||
|
|
@ -59,30 +59,16 @@ abstract class MailboxSpec extends
|
|||
}
|
||||
|
||||
"dequeue what was enqueued properly for unbounded mailboxes" in {
|
||||
testEnqueueDequeue(UnboundedMailbox(false))
|
||||
testEnqueueDequeue(UnboundedMailbox())
|
||||
}
|
||||
|
||||
"dequeue what was enqueued properly for bounded mailboxes" in {
|
||||
testEnqueueDequeue(BoundedMailbox(false, 10000, Duration(-1, TimeUnit.MILLISECONDS)))
|
||||
testEnqueueDequeue(BoundedMailbox(10000, Duration(-1, TimeUnit.MILLISECONDS)))
|
||||
}
|
||||
|
||||
"dequeue what was enqueued properly for bounded mailboxes with pushTimeout" in {
|
||||
testEnqueueDequeue(BoundedMailbox(false, 10000, Duration(100, TimeUnit.MILLISECONDS)))
|
||||
testEnqueueDequeue(BoundedMailbox(10000, Duration(100, TimeUnit.MILLISECONDS)))
|
||||
}
|
||||
|
||||
/** FIXME Adapt test so it works with the last dequeue
|
||||
|
||||
"dequeue what was enqueued properly for unbounded mailboxes with blockDeque" in {
|
||||
testEnqueueDequeue(UnboundedMailbox(true))
|
||||
}
|
||||
|
||||
"dequeue what was enqueued properly for bounded mailboxes with blockDeque" in {
|
||||
testEnqueueDequeue(BoundedMailbox(true, 1000, Duration(-1, TimeUnit.MILLISECONDS)))
|
||||
}
|
||||
|
||||
"dequeue what was enqueued properly for bounded mailboxes with blockDeque and pushTimeout" in {
|
||||
testEnqueueDequeue(BoundedMailbox(true, 1000, Duration(100, TimeUnit.MILLISECONDS)))
|
||||
}*/
|
||||
}
|
||||
|
||||
//CANDIDATE FOR TESTKIT
|
||||
|
|
@ -111,8 +97,8 @@ abstract class MailboxSpec extends
|
|||
q match {
|
||||
case aQueue: BlockingQueue[_] =>
|
||||
config match {
|
||||
case BoundedMailbox(_,capacity,_) => aQueue.remainingCapacity must be === capacity
|
||||
case UnboundedMailbox(_) => aQueue.remainingCapacity must be === Int.MaxValue
|
||||
case BoundedMailbox(capacity,_) => aQueue.remainingCapacity must be === capacity
|
||||
case UnboundedMailbox() => aQueue.remainingCapacity must be === Int.MaxValue
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
|
@ -165,10 +151,8 @@ abstract class MailboxSpec extends
|
|||
class DefaultMailboxSpec extends MailboxSpec {
|
||||
lazy val name = "The default mailbox implementation"
|
||||
def factory = {
|
||||
case UnboundedMailbox(blockDequeue) =>
|
||||
new DefaultUnboundedMessageQueue(blockDequeue)
|
||||
case BoundedMailbox(blocking, capacity, pushTimeOut) =>
|
||||
new DefaultBoundedMessageQueue(capacity, pushTimeOut, blocking)
|
||||
case UnboundedMailbox() => new DefaultUnboundedMessageQueue()
|
||||
case BoundedMailbox(capacity, pushTimeOut) => new DefaultBoundedMessageQueue(capacity, pushTimeOut)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,9 +160,7 @@ class PriorityMailboxSpec extends MailboxSpec {
|
|||
val comparator = PriorityGenerator(_.##)
|
||||
lazy val name = "The priority mailbox implementation"
|
||||
def factory = {
|
||||
case UnboundedMailbox(blockDequeue) =>
|
||||
new UnboundedPriorityMessageQueue(blockDequeue, comparator)
|
||||
case BoundedMailbox(blocking, capacity, pushTimeOut) =>
|
||||
new BoundedPriorityMessageQueue(capacity, pushTimeOut, blocking, comparator)
|
||||
case UnboundedMailbox() => new UnboundedPriorityMessageQueue(comparator)
|
||||
case BoundedMailbox(capacity, pushTimeOut) => new BoundedPriorityMessageQueue(capacity, pushTimeOut, comparator)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,11 +10,11 @@ class PriorityDispatcherSpec extends WordSpec with MustMatchers {
|
|||
|
||||
"A PriorityExecutorBasedEventDrivenDispatcher" must {
|
||||
"Order it's messages according to the specified comparator using an unbounded mailbox" in {
|
||||
testOrdering(UnboundedMailbox(false))
|
||||
testOrdering(UnboundedMailbox())
|
||||
}
|
||||
|
||||
"Order it's messages according to the specified comparator using a bounded mailbox" in {
|
||||
testOrdering(BoundedMailbox(false,1000))
|
||||
testOrdering(BoundedMailbox(1000))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import java.net.{InetAddress, UnknownHostException}
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class AkkaException(message: String = "") extends RuntimeException(message) with Serializable {
|
||||
class AkkaException(message: String = "", cause: Throwable = null) extends RuntimeException(message, cause) with Serializable {
|
||||
val uuid = "%s_%s".format(AkkaException.hostname, newUuid)
|
||||
|
||||
override lazy val toString = {
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ case class MaximumNumberOfRestartsWithinTimeRangeReached(
|
|||
@BeanProperty val lastExceptionCausingRestart: Throwable) extends LifeCycleMessage
|
||||
|
||||
// Exceptions for Actors
|
||||
class ActorStartException private[akka](message: String) extends AkkaException(message)
|
||||
class IllegalActorStateException private[akka](message: String) extends AkkaException(message)
|
||||
class ActorKilledException private[akka](message: String) extends AkkaException(message)
|
||||
class ActorInitializationException private[akka](message: String) extends AkkaException(message)
|
||||
class ActorTimeoutException private[akka](message: String) extends AkkaException(message)
|
||||
class InvalidMessageException private[akka](message: String) extends AkkaException(message)
|
||||
class ActorStartException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class IllegalActorStateException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class ActorKilledException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class ActorInitializationException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class ActorTimeoutException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class InvalidMessageException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* This message is thrown by default when an Actors behavior doesn't match a message
|
||||
|
|
@ -125,7 +125,9 @@ object Actor extends ListenerManagement {
|
|||
*/
|
||||
type Receive = PartialFunction[Any, Unit]
|
||||
|
||||
private[actor] val actorRefInCreation = new scala.util.DynamicVariable[Option[ActorRef]](None)
|
||||
private[actor] val actorRefInCreation = new ThreadLocal[Option[ActorRef]]{
|
||||
override def initialValue = None
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ActorRef out of the Actor with type T.
|
||||
|
|
@ -159,12 +161,15 @@ object Actor extends ListenerManagement {
|
|||
*/
|
||||
def actorOf(clazz: Class[_ <: Actor]): ActorRef = new LocalActorRef(() => {
|
||||
import ReflectiveAccess.{ createInstance, noParams, noArgs }
|
||||
createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs).getOrElse(
|
||||
throw new ActorInitializationException(
|
||||
createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs) match {
|
||||
case r: Right[Exception, Actor] => r.b
|
||||
case l: Left[Exception, Actor] => throw new ActorInitializationException(
|
||||
"Could not instantiate Actor of " + clazz +
|
||||
"\nMake sure Actor is NOT defined inside a class/trait," +
|
||||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'."))
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a)
|
||||
}
|
||||
|
||||
}, None)
|
||||
|
||||
/**
|
||||
|
|
@ -290,7 +295,7 @@ trait Actor {
|
|||
* the 'forward' function.
|
||||
*/
|
||||
@transient implicit val someSelf: Some[ActorRef] = {
|
||||
val optRef = Actor.actorRefInCreation.value
|
||||
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'." +
|
||||
|
|
@ -298,7 +303,7 @@ trait Actor {
|
|||
"\n\tEither use:" +
|
||||
"\n\t\t'val actor = Actor.actorOf[MyActor]', or" +
|
||||
"\n\t\t'val actor = Actor.actorOf(new MyActor(..))'")
|
||||
Actor.actorRefInCreation.value = None
|
||||
Actor.actorRefInCreation.set(None)
|
||||
optRef.asInstanceOf[Some[ActorRef]].get.id = getClass.getName //FIXME: Is this needed?
|
||||
optRef.asInstanceOf[Some[ActorRef]]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1015,12 +1015,12 @@ class LocalActorRef private[akka] (
|
|||
|
||||
private[this] def newActor: Actor = {
|
||||
try {
|
||||
Actor.actorRefInCreation.value = Some(this)
|
||||
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
|
||||
} finally {
|
||||
Actor.actorRefInCreation.value = None
|
||||
Actor.actorRefInCreation.set(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ trait BootableActorLoaderService extends Bootable {
|
|||
|
||||
protected def createApplicationClassLoader : Option[ClassLoader] = Some({
|
||||
if (HOME.isDefined) {
|
||||
val CONFIG = HOME.get + "/config"
|
||||
val DEPLOY = HOME.get + "/deploy"
|
||||
val DEPLOY_DIR = new File(DEPLOY)
|
||||
if (!DEPLOY_DIR.exists) {
|
||||
|
|
|
|||
|
|
@ -105,13 +105,17 @@ object Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
def shutdown: Unit = synchronized {
|
||||
service.shutdown
|
||||
def shutdown() {
|
||||
synchronized {
|
||||
service.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
def restart: Unit = synchronized {
|
||||
shutdown
|
||||
service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory)
|
||||
def restart() {
|
||||
synchronized {
|
||||
shutdown()
|
||||
service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import java.util.concurrent.{CopyOnWriteArrayList, ConcurrentHashMap}
|
|||
import java.net.InetSocketAddress
|
||||
import akka.config.Supervision._
|
||||
|
||||
class SupervisorException private[akka](message: String) extends AkkaException(message)
|
||||
class SupervisorException private[akka](message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* Factory object for creating supervisors declarative. It creates instances of the 'Supervisor' class.
|
||||
|
|
|
|||
|
|
@ -88,14 +88,14 @@ abstract class UntypedActor extends Actor {
|
|||
* <p/>
|
||||
* Is called when an Actor is started by invoking 'actor.start()'.
|
||||
*/
|
||||
override def preStart {}
|
||||
override def preStart() {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when 'actor.stop()' is invoked.
|
||||
*/
|
||||
override def postStop {}
|
||||
override def postStop() {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ package akka.config
|
|||
|
||||
import akka.AkkaException
|
||||
|
||||
class ConfigurationException(message: String) extends AkkaException(message)
|
||||
class ModuleNotAvailableException(message: String) extends AkkaException(message)
|
||||
class ConfigurationException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
class ModuleNotAvailableException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* Loads up the configuration (from the akka.conf file).
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ object DataFlow {
|
|||
object Start
|
||||
object Exit
|
||||
|
||||
class DataFlowVariableException(msg: String) extends AkkaException(msg)
|
||||
class DataFlowVariableException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* Executes the supplied thunk in another thread.
|
||||
|
|
@ -160,6 +160,6 @@ object DataFlow {
|
|||
}
|
||||
}
|
||||
|
||||
def shutdown = in ! Exit
|
||||
def shutdown() { in ! Exit }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,14 +187,14 @@ object Dispatchers {
|
|||
case "GlobalExecutorBasedEventDriven" => GlobalExecutorBasedEventDrivenDispatcherConfigurator
|
||||
case fqn =>
|
||||
ReflectiveAccess.getClassFor[MessageDispatcherConfigurator](fqn) match {
|
||||
case Some(clazz) =>
|
||||
val instance = ReflectiveAccess.createInstance[MessageDispatcherConfigurator](clazz, Array[Class[_]](), Array[AnyRef]())
|
||||
if (instance.isEmpty)
|
||||
throw new IllegalArgumentException("Cannot instantiate MessageDispatcherConfigurator type [%s], make sure it has a default no-args constructor" format fqn)
|
||||
else
|
||||
instance.get
|
||||
case None =>
|
||||
throw new IllegalArgumentException("Unknown MessageDispatcherConfigurator type [%s]" format fqn)
|
||||
case r: Right[_, Class[MessageDispatcherConfigurator]] =>
|
||||
ReflectiveAccess.createInstance[MessageDispatcherConfigurator](r.b, Array[Class[_]](), Array[AnyRef]()) match {
|
||||
case r: Right[Exception, MessageDispatcherConfigurator] => r.b
|
||||
case l: Left[Exception, MessageDispatcherConfigurator] =>
|
||||
throw new IllegalArgumentException("Cannot instantiate MessageDispatcherConfigurator type [%s], make sure it has a default no-args constructor" format fqn, l.a)
|
||||
}
|
||||
case l: Left[Exception, _] =>
|
||||
throw new IllegalArgumentException("Unknown MessageDispatcherConfigurator type [%s]" format fqn, l.a)
|
||||
}
|
||||
} map {
|
||||
_ configure cfg
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class ExecutorBasedEventDrivenDispatcher(
|
|||
registerForExecution(mbox)
|
||||
}
|
||||
|
||||
private[akka] def executeFuture(invocation: FutureInvocation): Unit = if (active.isOn) {
|
||||
private[akka] def executeFuture(invocation: FutureInvocation[_]): Unit = if (active.isOn) {
|
||||
try executorService.get() execute invocation
|
||||
catch {
|
||||
case e: RejectedExecutionException =>
|
||||
|
|
@ -117,20 +117,14 @@ class ExecutorBasedEventDrivenDispatcher(
|
|||
|
||||
def createMailbox(actorRef: ActorRef): AnyRef = mailboxType match {
|
||||
case b: UnboundedMailbox =>
|
||||
if (b.blocking) {
|
||||
new DefaultUnboundedMessageQueue(true) with ExecutableMailbox {
|
||||
final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
|
||||
}
|
||||
} else { //If we have an unbounded, non-blocking mailbox, we can go lockless
|
||||
new ConcurrentLinkedQueue[MessageInvocation] with MessageQueue with ExecutableMailbox {
|
||||
final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
|
||||
final def enqueue(m: MessageInvocation) = this.add(m)
|
||||
final def dequeue(): MessageInvocation = this.poll()
|
||||
}
|
||||
new ConcurrentLinkedQueue[MessageInvocation] with MessageQueue with ExecutableMailbox {
|
||||
@inline final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
|
||||
@inline final def enqueue(m: MessageInvocation) = this.add(m)
|
||||
@inline final def dequeue(): MessageInvocation = this.poll()
|
||||
}
|
||||
case b: BoundedMailbox =>
|
||||
new DefaultBoundedMessageQueue(b.capacity, b.pushTimeOut, b.blocking) with ExecutableMailbox {
|
||||
final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
|
||||
new DefaultBoundedMessageQueue(b.capacity, b.pushTimeOut) with ExecutableMailbox {
|
||||
@inline final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,13 +288,13 @@ trait PriorityMailbox { self: ExecutorBasedEventDrivenDispatcher =>
|
|||
|
||||
override def createMailbox(actorRef: ActorRef): AnyRef = self.mailboxType match {
|
||||
case b: UnboundedMailbox =>
|
||||
new UnboundedPriorityMessageQueue(b.blocking, comparator) with ExecutableMailbox {
|
||||
final def dispatcher = self
|
||||
new UnboundedPriorityMessageQueue(comparator) with ExecutableMailbox {
|
||||
@inline final def dispatcher = self
|
||||
}
|
||||
|
||||
case b: BoundedMailbox =>
|
||||
new BoundedPriorityMessageQueue(b.capacity, b.pushTimeOut, b.blocking, comparator) with ExecutableMailbox {
|
||||
final def dispatcher = self
|
||||
new BoundedPriorityMessageQueue(b.capacity, b.pushTimeOut, comparator) with ExecutableMailbox {
|
||||
@inline final def dispatcher = self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import akka.util.{ReflectiveAccess, Switch}
|
|||
import java.util.Queue
|
||||
import java.util.concurrent.atomic.{AtomicReference, AtomicInteger}
|
||||
import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue, LinkedBlockingQueue}
|
||||
import util.DynamicVariable
|
||||
|
||||
/**
|
||||
* An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed
|
||||
|
|
@ -55,6 +56,7 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(
|
|||
|
||||
@volatile private var actorType: Option[Class[_]] = None
|
||||
@volatile private var members = Vector[ActorRef]()
|
||||
private val donationInProgress = new DynamicVariable(false)
|
||||
|
||||
private[akka] override def register(actorRef: ActorRef) = {
|
||||
//Verify actor type conformity
|
||||
|
|
@ -78,18 +80,22 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(
|
|||
|
||||
override private[akka] def dispatch(invocation: MessageInvocation) = {
|
||||
val mbox = getMailbox(invocation.receiver)
|
||||
/*if (!mbox.isEmpty && attemptDonationOf(invocation, mbox)) {
|
||||
if (donationInProgress.value == false && (!mbox.isEmpty || mbox.dispatcherLock.locked) && attemptDonationOf(invocation, mbox)) {
|
||||
//We were busy and we got to donate the message to some other lucky guy, we're done here
|
||||
} else {*/
|
||||
} else {
|
||||
mbox enqueue invocation
|
||||
registerForExecution(mbox)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
override private[akka] def reRegisterForExecution(mbox: MessageQueue with ExecutableMailbox): Unit = {
|
||||
while(donateFrom(mbox)) {} //When we reregister, first donate messages to another actor
|
||||
try {
|
||||
donationInProgress.value = true
|
||||
while(donateFrom(mbox)) {} //When we reregister, first donate messages to another actor
|
||||
} finally { donationInProgress.value = false }
|
||||
|
||||
if (!mbox.isEmpty) //If we still have messages left to process, reschedule for execution
|
||||
super.reRegisterForExecution(mbox)
|
||||
super.reRegisterForExecution(mbox)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -110,13 +116,14 @@ class ExecutorBasedEventDrivenWorkStealingDispatcher(
|
|||
/**
|
||||
* Returns true if the donation succeeded or false otherwise
|
||||
*/
|
||||
/*protected def attemptDonationOf(message: MessageInvocation, donorMbox: MessageQueue with ExecutableMailbox): Boolean = {
|
||||
protected def attemptDonationOf(message: MessageInvocation, donorMbox: MessageQueue with ExecutableMailbox): Boolean = try {
|
||||
donationInProgress.value = true
|
||||
val actors = members // copy to prevent concurrent modifications having any impact
|
||||
doFindDonorRecipient(donorMbox, actors, System.identityHashCode(message) % actors.size) match {
|
||||
case null => false
|
||||
case recipient => donate(message, recipient)
|
||||
}
|
||||
}*/
|
||||
} finally { donationInProgress.value = false }
|
||||
|
||||
/**
|
||||
* Rewrites the message and adds that message to the recipients mailbox
|
||||
|
|
|
|||
|
|
@ -7,18 +7,21 @@ package akka.dispatch
|
|||
import akka.AkkaException
|
||||
import akka.event.EventHandler
|
||||
import akka.actor.{Actor, Channel}
|
||||
import akka.routing.Dispatcher
|
||||
import akka.util.Duration
|
||||
import akka.japi.{ Procedure, Function => JFunc }
|
||||
|
||||
import scala.util.continuations._
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.concurrent. {ConcurrentLinkedQueue, TimeUnit, Callable}
|
||||
import java.util.concurrent.TimeUnit.{NANOSECONDS => NANOS, MILLISECONDS => MILLIS}
|
||||
import java.util.concurrent.atomic. {AtomicBoolean, AtomicInteger}
|
||||
import java.util.concurrent.atomic. {AtomicBoolean}
|
||||
import java.lang.{Iterable => JIterable}
|
||||
import java.util.{LinkedList => JLinkedList}
|
||||
import scala.collection.mutable.Stack
|
||||
import annotation.tailrec
|
||||
|
||||
class FutureTimeoutException(message: String) extends AkkaException(message)
|
||||
class FutureTimeoutException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
object Futures {
|
||||
|
||||
|
|
@ -199,7 +202,7 @@ object Futures {
|
|||
// =====================================
|
||||
// Deprecations
|
||||
// =====================================
|
||||
|
||||
|
||||
/**
|
||||
* (Blocking!)
|
||||
*/
|
||||
|
|
@ -232,11 +235,8 @@ object Future {
|
|||
* This method constructs and returns a Future that will eventually hold the result of the execution of the supplied body
|
||||
* The execution is performed by the specified Dispatcher.
|
||||
*/
|
||||
def apply[T](body: => T, timeout: Long = Actor.TIMEOUT)(implicit dispatcher: MessageDispatcher): Future[T] = {
|
||||
val f = new DefaultCompletableFuture[T](timeout)
|
||||
dispatcher.dispatchFuture(FutureInvocation(f.asInstanceOf[CompletableFuture[Any]], () => body))
|
||||
f
|
||||
}
|
||||
def apply[T](body: => T, timeout: Long = Actor.TIMEOUT)(implicit dispatcher: MessageDispatcher): Future[T] =
|
||||
dispatcher.dispatchFuture(() => body, timeout)
|
||||
|
||||
/**
|
||||
* Construct a completable channel
|
||||
|
|
@ -274,22 +274,60 @@ object Future {
|
|||
val fb = fn(a.asInstanceOf[A])
|
||||
for (r <- fr; b <-fb) yield (r += b)
|
||||
}.map(_.result)
|
||||
|
||||
/**
|
||||
* Captures a block that will be transformed into 'Continuation Passing Style' using Scala's Delimited
|
||||
* Continuations plugin.
|
||||
*
|
||||
* Within the block, the result of a Future may be accessed by calling Future.apply. At that point
|
||||
* execution is suspended with the rest of the block being stored in a continuation until the result
|
||||
* of the Future is available. If an Exception is thrown while processing, it will be contained
|
||||
* within the resulting Future.
|
||||
*
|
||||
* This allows working with Futures in an imperative style without blocking for each result.
|
||||
*
|
||||
* Completing a Future using 'CompletableFuture << Future' will also suspend execution until the
|
||||
* value of the other Future is available.
|
||||
*
|
||||
* The Delimited Continuations compiler plugin must be enabled in order to use this method.
|
||||
*/
|
||||
def flow[A](body: => A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = {
|
||||
val future = new DefaultCompletableFuture[A](timeout)
|
||||
(reset(future.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)): Future[Any]) onComplete { f =>
|
||||
val opte = f.exception
|
||||
if (opte.isDefined) future completeWithException (opte.get)
|
||||
}
|
||||
future
|
||||
}
|
||||
|
||||
private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() => Unit]]]() {
|
||||
override def initialValue = None
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait Future[+T] {
|
||||
|
||||
/**
|
||||
* Returns the result of this future after waiting for it to complete,
|
||||
* this method will throw any throwable that this Future was completed with
|
||||
* and will throw a java.util.concurrent.TimeoutException if there is no result
|
||||
* within the Futures timeout
|
||||
* For use only within a Future.flow block or another compatible Delimited Continuations reset block.
|
||||
*
|
||||
* Returns the result of this Future without blocking, by suspending execution and storing it as a
|
||||
* 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
|
||||
* an untyped Future.
|
||||
*/
|
||||
def apply(): T = this.await.resultOrException.get
|
||||
def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap (_: A => Future[Any]))
|
||||
|
||||
/**
|
||||
* Java API for apply()
|
||||
* Blocks awaiting completion of this Future, then returns the resulting value,
|
||||
* or throws the completed exception
|
||||
*
|
||||
* Scala & Java API
|
||||
*
|
||||
* throws FutureTimeoutException if this Future times out when waiting for completion
|
||||
*/
|
||||
def get: T = apply()
|
||||
def get: T = this.await.resultOrException.get
|
||||
|
||||
/**
|
||||
* Blocks the current thread until the Future has been completed or the
|
||||
|
|
@ -298,11 +336,20 @@ sealed trait Future[+T] {
|
|||
*/
|
||||
def await : Future[T]
|
||||
|
||||
/**
|
||||
* Blocks the current thread until the Future has been completed or the
|
||||
* timeout has expired. The timeout will be the least value of 'atMost' and the timeout
|
||||
* supplied at the constructuion of this Future.
|
||||
* In the case of the timeout expiring a FutureTimeoutException will be thrown.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@deprecated("Will be removed after 1.1, it's dangerous and can cause deadlocks, agony and insanity.")
|
||||
def awaitBlocking : Future[T]
|
||||
|
||||
/**
|
||||
|
|
@ -340,24 +387,6 @@ sealed trait Future[+T] {
|
|||
else None
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the completion of this Future, then returns the completed value.
|
||||
* If the Future's timeout expires while waiting a FutureTimeoutException
|
||||
* will be thrown.
|
||||
*
|
||||
* Equivalent to calling future.await.value.
|
||||
*/
|
||||
def awaitValue: Option[Either[Throwable, T]]
|
||||
|
||||
/**
|
||||
* Returns the result of the Future if one is available within the specified
|
||||
* time, if the time left on the future is less than the specified time, the
|
||||
* time left on the future will be used instead of the specified time.
|
||||
* returns None if no result, Some(Right(t)) if a result, or
|
||||
* Some(Left(error)) if there was an exception
|
||||
*/
|
||||
def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]]
|
||||
|
||||
/**
|
||||
* Returns the contained exception of this Future if it exists.
|
||||
*/
|
||||
|
|
@ -409,21 +438,18 @@ sealed trait Future[+T] {
|
|||
final def collect[A](pf: PartialFunction[Any, A]): Future[A] = {
|
||||
val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS)
|
||||
onComplete { ft =>
|
||||
val optv = ft.value
|
||||
if (optv.isDefined) {
|
||||
val v = optv.get
|
||||
fa complete {
|
||||
if (v.isLeft) v.asInstanceOf[Either[Throwable, A]]
|
||||
else {
|
||||
try {
|
||||
val r = v.right.get
|
||||
if (pf isDefinedAt r) Right(pf(r))
|
||||
else Left(new MatchError(r))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
}
|
||||
val v = ft.value.get
|
||||
fa complete {
|
||||
if (v.isLeft) v.asInstanceOf[Either[Throwable, A]]
|
||||
else {
|
||||
try {
|
||||
val r = v.right.get
|
||||
if (pf isDefinedAt r) Right(pf(r))
|
||||
else Left(new MatchError(r))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -431,6 +457,36 @@ sealed trait Future[+T] {
|
|||
fa
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Future that will handle any matching Throwable that this
|
||||
* Future might contain. If there is no match, or if this Future contains
|
||||
* 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
|
||||
* </pre>
|
||||
*/
|
||||
final def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = {
|
||||
val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS)
|
||||
onComplete { ft =>
|
||||
val opte = ft.exception
|
||||
fa complete {
|
||||
if (opte.isDefined) {
|
||||
val e = opte.get
|
||||
try {
|
||||
if (pf isDefinedAt e) Right(pf(e))
|
||||
else Left(e)
|
||||
} catch {
|
||||
case x: Exception => Left(x)
|
||||
}
|
||||
} else ft.value.get
|
||||
}
|
||||
}
|
||||
fa
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Future by applying a function to the successful result of
|
||||
* this Future. If this Future is completed with an exception then the new
|
||||
|
|
@ -456,7 +512,7 @@ sealed trait Future[+T] {
|
|||
fa complete (try {
|
||||
Right(f(v.right.get))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
})
|
||||
|
|
@ -492,7 +548,7 @@ sealed trait Future[+T] {
|
|||
try {
|
||||
fa.completeWith(f(v.right.get))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
fa completeWithException e
|
||||
}
|
||||
|
|
@ -508,7 +564,7 @@ sealed trait Future[+T] {
|
|||
f(optr.get)
|
||||
}
|
||||
|
||||
final def filter(p: T => Boolean): Future[T] = {
|
||||
final def filter(p: Any => Boolean): Future[Any] = {
|
||||
val f = new DefaultCompletableFuture[T](timeoutInNanos, NANOS)
|
||||
onComplete { ft =>
|
||||
val optv = ft.value
|
||||
|
|
@ -522,7 +578,7 @@ sealed trait Future[+T] {
|
|||
if (p(r)) Right(r)
|
||||
else Left(new MatchError(r))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
})
|
||||
|
|
@ -553,7 +609,7 @@ sealed trait Future[+T] {
|
|||
|
||||
final def foreach[A >: T](proc: Procedure[A]): Unit = foreach(proc(_))
|
||||
|
||||
final def filter[A >: T](p: JFunc[A,Boolean]): Future[T] = filter(p(_))
|
||||
final def filter(p: JFunc[Any,Boolean]): Future[Any] = filter(p(_))
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -594,10 +650,20 @@ trait CompletableFuture[T] extends Future[T] {
|
|||
*/
|
||||
final def << (value: T): Future[T] = complete(Right(value))
|
||||
|
||||
/**
|
||||
* Alias for completeWith(other).
|
||||
*/
|
||||
final def << (other : Future[T]): Future[T] = completeWith(other)
|
||||
final def << (other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] => Future[Any]) =>
|
||||
val fr = new DefaultCompletableFuture[Any](Actor.TIMEOUT)
|
||||
this completeWith other onComplete { f =>
|
||||
try {
|
||||
fr completeWith cont(f)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
fr completeWithException e
|
||||
}
|
||||
}
|
||||
fr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -616,45 +682,34 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
private var _value: Option[Either[Throwable, T]] = None
|
||||
private var _listeners: List[Future[T] => Unit] = Nil
|
||||
|
||||
/**
|
||||
* Must be called inside _lock.lock<->_lock.unlock
|
||||
*/
|
||||
@tailrec
|
||||
private def awaitUnsafe(wait: Long): Boolean = {
|
||||
if (_value.isEmpty && wait > 0) {
|
||||
private def awaitUnsafe(waitTimeNanos: Long): Boolean = {
|
||||
if (_value.isEmpty && waitTimeNanos > 0) {
|
||||
val start = currentTimeInNanos
|
||||
val remaining = try {
|
||||
_signal.awaitNanos(wait)
|
||||
val remainingNanos = try {
|
||||
_signal.awaitNanos(waitTimeNanos)
|
||||
} catch {
|
||||
case e: InterruptedException =>
|
||||
wait - (currentTimeInNanos - start)
|
||||
waitTimeNanos - (currentTimeInNanos - start)
|
||||
}
|
||||
awaitUnsafe(remaining)
|
||||
awaitUnsafe(remainingNanos)
|
||||
} else {
|
||||
_value.isDefined
|
||||
}
|
||||
}
|
||||
|
||||
def awaitValue: Option[Either[Throwable, T]] = {
|
||||
def await(atMost: Duration) = {
|
||||
_lock.lock
|
||||
try {
|
||||
awaitUnsafe(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos))
|
||||
_value
|
||||
} finally {
|
||||
_lock.unlock
|
||||
}
|
||||
}
|
||||
|
||||
def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] = {
|
||||
_lock.lock
|
||||
try {
|
||||
awaitUnsafe(unit.toNanos(time).min(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)))
|
||||
_value
|
||||
} finally {
|
||||
_lock.unlock
|
||||
}
|
||||
if (try { awaitUnsafe(atMost.toNanos min timeLeft()) } finally { _lock.unlock }) this
|
||||
else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(timeoutInNanos) + "] milliseconds")
|
||||
}
|
||||
|
||||
def await = {
|
||||
_lock.lock
|
||||
if (try { awaitUnsafe(timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)) } finally { _lock.unlock }) this
|
||||
if (try { awaitUnsafe(timeLeft()) } finally { _lock.unlock }) this
|
||||
else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(timeoutInNanos) + "] milliseconds")
|
||||
}
|
||||
|
||||
|
|
@ -670,7 +725,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
}
|
||||
}
|
||||
|
||||
def isExpired: Boolean = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos) <= 0
|
||||
def isExpired: Boolean = timeLeft() <= 0
|
||||
|
||||
def value: Option[Either[Throwable, T]] = {
|
||||
_lock.lock
|
||||
|
|
@ -684,7 +739,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
def complete(value: Either[Throwable, T]): DefaultCompletableFuture[T] = {
|
||||
_lock.lock
|
||||
val notifyTheseListeners = try {
|
||||
if (_value.isEmpty) {
|
||||
if (_value.isEmpty && !isExpired) { //Only complete if we aren't expired
|
||||
_value = Some(value)
|
||||
val existingListeners = _listeners
|
||||
_listeners = Nil
|
||||
|
|
@ -695,8 +750,29 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
_lock.unlock
|
||||
}
|
||||
|
||||
if (notifyTheseListeners.nonEmpty)
|
||||
notifyTheseListeners.reverse foreach notify
|
||||
if (notifyTheseListeners.nonEmpty) { // Steps to ensure we don't run into a stack-overflow situation
|
||||
@tailrec def runCallbacks(rest: List[Future[T] => Unit], callbacks: Stack[() => Unit]) {
|
||||
if (rest.nonEmpty) {
|
||||
notifyCompleted(rest.head)
|
||||
while (callbacks.nonEmpty) { callbacks.pop().apply() }
|
||||
runCallbacks(rest.tail, callbacks)
|
||||
}
|
||||
}
|
||||
|
||||
val pending = Future.callbacksPendingExecution.get
|
||||
if (pending.isDefined) { //Instead of nesting the calls to the callbacks (leading to stack overflow)
|
||||
pending.get.push(() => { // Linearize/aggregate callbacks at top level and then execute
|
||||
val doNotify = notifyCompleted _ //Hoist closure to avoid garbage
|
||||
notifyTheseListeners foreach doNotify
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
val callbacks = Stack[() => Unit]() // Allocate new aggregator for pending callbacks
|
||||
Future.callbacksPendingExecution.set(Some(callbacks)) // Specify the callback aggregator
|
||||
runCallbacks(notifyTheseListeners, callbacks) // Execute callbacks, if they trigger new callbacks, they are aggregated
|
||||
} finally { Future.callbacksPendingExecution.set(None) } // Ensure cleanup
|
||||
}
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
|
@ -705,19 +781,21 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
_lock.lock
|
||||
val notifyNow = try {
|
||||
if (_value.isEmpty) {
|
||||
_listeners ::= func
|
||||
false
|
||||
if(!isExpired) { //Only add the listener if the future isn't expired
|
||||
_listeners ::= func
|
||||
false
|
||||
} else false //Will never run the callback since the future is expired
|
||||
} else true
|
||||
} finally {
|
||||
_lock.unlock
|
||||
}
|
||||
|
||||
if (notifyNow) notify(func)
|
||||
if (notifyNow) notifyCompleted(func)
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
private def notify(func: Future[T] => Unit) {
|
||||
private def notifyCompleted(func: Future[T] => Unit) {
|
||||
try {
|
||||
func(this)
|
||||
} catch {
|
||||
|
|
@ -725,7 +803,8 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com
|
|||
}
|
||||
}
|
||||
|
||||
private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis)
|
||||
@inline private def currentTimeInNanos: Long = MILLIS.toNanos(System.currentTimeMillis)
|
||||
@inline private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -737,8 +816,7 @@ sealed class AlreadyCompletedFuture[T](suppliedValue: Either[Throwable, T]) exte
|
|||
|
||||
def complete(value: Either[Throwable, T]): CompletableFuture[T] = this
|
||||
def onComplete(func: Future[T] => Unit): Future[T] = { func(this); this }
|
||||
def awaitValue: Option[Either[Throwable, T]] = value
|
||||
def valueWithin(time: Long, unit: TimeUnit): Option[Either[Throwable, T]] = value
|
||||
def await(atMost: Duration): Future[T] = this
|
||||
def await : Future[T] = this
|
||||
def awaitBlocking : Future[T] = this
|
||||
def isExpired: Boolean = true
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import java.util.{Queue, List, Comparator, PriorityQueue}
|
|||
import java.util.concurrent._
|
||||
import akka.util._
|
||||
|
||||
class MessageQueueAppendFailedException(message: String) extends AkkaException(message)
|
||||
class MessageQueueAppendFailedException(message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
|
|
@ -30,9 +30,8 @@ trait MessageQueue {
|
|||
*/
|
||||
sealed trait MailboxType
|
||||
|
||||
case class UnboundedMailbox(val blocking: Boolean = false) extends MailboxType
|
||||
case class UnboundedMailbox() extends MailboxType
|
||||
case class BoundedMailbox(
|
||||
val blocking: Boolean = false,
|
||||
val capacity: Int = { if (Dispatchers.MAILBOX_CAPACITY < 0) Int.MaxValue else Dispatchers.MAILBOX_CAPACITY },
|
||||
val pushTimeOut: Duration = Dispatchers.MAILBOX_PUSH_TIME_OUT) extends MailboxType {
|
||||
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
||||
|
|
@ -40,46 +39,35 @@ case class BoundedMailbox(
|
|||
}
|
||||
|
||||
trait UnboundedMessageQueueSemantics extends MessageQueue { self: BlockingQueue[MessageInvocation] =>
|
||||
def blockDequeue: Boolean
|
||||
|
||||
final def enqueue(handle: MessageInvocation) {
|
||||
this add handle
|
||||
}
|
||||
|
||||
final def dequeue(): MessageInvocation = {
|
||||
if (blockDequeue) this.take()
|
||||
else this.poll()
|
||||
}
|
||||
@inline final def enqueue(handle: MessageInvocation): Unit = this add handle
|
||||
@inline final def dequeue(): MessageInvocation = this.poll()
|
||||
}
|
||||
|
||||
trait BoundedMessageQueueSemantics extends MessageQueue { self: BlockingQueue[MessageInvocation] =>
|
||||
def blockDequeue: Boolean
|
||||
def pushTimeOut: Duration
|
||||
|
||||
final def enqueue(handle: MessageInvocation) {
|
||||
if (pushTimeOut.length > 0 && pushTimeOut.toMillis > 0) {
|
||||
if (!this.offer(handle, pushTimeOut.length, pushTimeOut.unit))
|
||||
throw new MessageQueueAppendFailedException("Couldn't enqueue message " + handle + " to " + toString)
|
||||
if (pushTimeOut.length > 0) {
|
||||
this.offer(handle, pushTimeOut.length, pushTimeOut.unit) || {
|
||||
throw new MessageQueueAppendFailedException("Couldn't enqueue message " + handle + " to " + toString) }
|
||||
} else this put handle
|
||||
}
|
||||
|
||||
final def dequeue(): MessageInvocation =
|
||||
if (blockDequeue) this.take()
|
||||
else this.poll()
|
||||
@inline final def dequeue(): MessageInvocation = this.poll()
|
||||
}
|
||||
|
||||
class DefaultUnboundedMessageQueue(val blockDequeue: Boolean) extends
|
||||
class DefaultUnboundedMessageQueue extends
|
||||
LinkedBlockingQueue[MessageInvocation] with
|
||||
UnboundedMessageQueueSemantics
|
||||
|
||||
class DefaultBoundedMessageQueue(capacity: Int, val pushTimeOut: Duration, val blockDequeue: Boolean) extends
|
||||
class DefaultBoundedMessageQueue(capacity: Int, val pushTimeOut: Duration) extends
|
||||
LinkedBlockingQueue[MessageInvocation](capacity) with
|
||||
BoundedMessageQueueSemantics
|
||||
|
||||
class UnboundedPriorityMessageQueue(val blockDequeue: Boolean, cmp: Comparator[MessageInvocation]) extends
|
||||
class UnboundedPriorityMessageQueue(cmp: Comparator[MessageInvocation]) extends
|
||||
PriorityBlockingQueue[MessageInvocation](11, cmp) with
|
||||
UnboundedMessageQueueSemantics
|
||||
|
||||
class BoundedPriorityMessageQueue(capacity: Int, val pushTimeOut: Duration, val blockDequeue: Boolean, cmp: Comparator[MessageInvocation]) extends
|
||||
BoundedBlockingQueue[MessageInvocation](capacity, new PriorityQueue[MessageInvocation](11, cmp)) with
|
||||
BoundedMessageQueueSemantics
|
||||
class BoundedPriorityMessageQueue(capacity: Int, val pushTimeOut: Duration, cmp: Comparator[MessageInvocation]) extends
|
||||
BoundedBlockingQueue[MessageInvocation](capacity, new PriorityQueue[MessageInvocation](11, cmp)) with
|
||||
BoundedMessageQueueSemantics
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package akka.dispatch
|
||||
|
||||
import java.util.concurrent._
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import akka.event.EventHandler
|
||||
import akka.config.Configuration
|
||||
import akka.config.Config.TIME_UNIT
|
||||
|
|
@ -29,16 +30,18 @@ final case class MessageInvocation(val receiver: ActorRef,
|
|||
}
|
||||
}
|
||||
|
||||
final case class FutureInvocation(future: CompletableFuture[Any], function: () => Any) extends Runnable {
|
||||
val uuid = akka.actor.newUuid
|
||||
|
||||
def run = future complete (try {
|
||||
Right(function.apply)
|
||||
} catch {
|
||||
case e =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
})
|
||||
final case class FutureInvocation[T](future: CompletableFuture[T], function: () => T, cleanup: () => Unit) extends Runnable {
|
||||
def run = {
|
||||
future complete (try {
|
||||
Right(function())
|
||||
} catch {
|
||||
case e =>
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(e)
|
||||
} finally {
|
||||
cleanup()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
object MessageDispatcher {
|
||||
|
|
@ -56,7 +59,7 @@ trait MessageDispatcher {
|
|||
import MessageDispatcher._
|
||||
|
||||
protected val uuids = new ConcurrentSkipListSet[Uuid]
|
||||
protected val futures = new ConcurrentSkipListSet[Uuid]
|
||||
protected val futures = new AtomicLong(0L)
|
||||
protected val guard = new ReentrantGuard
|
||||
protected val active = new Switch(false)
|
||||
|
||||
|
|
@ -83,15 +86,27 @@ trait MessageDispatcher {
|
|||
|
||||
private[akka] final def dispatchMessage(invocation: MessageInvocation): Unit = dispatch(invocation)
|
||||
|
||||
private[akka] final def dispatchFuture(invocation: FutureInvocation): Unit = {
|
||||
guard withGuard {
|
||||
futures add invocation.uuid
|
||||
if (active.isOff) { active.switchOn { start } }
|
||||
private[akka] final def dispatchFuture[T](block: () => T, timeout: Long): Future[T] = {
|
||||
futures.getAndIncrement()
|
||||
try {
|
||||
val future = new DefaultCompletableFuture[T](timeout)
|
||||
|
||||
if (active.isOff)
|
||||
guard withGuard { active.switchOn { start } }
|
||||
|
||||
executeFuture(FutureInvocation[T](future, block, futureCleanup))
|
||||
future
|
||||
} catch {
|
||||
case e =>
|
||||
futures.decrementAndGet
|
||||
throw e
|
||||
}
|
||||
invocation.future.onComplete { f =>
|
||||
}
|
||||
|
||||
private val futureCleanup: () => Unit =
|
||||
() => if (futures.decrementAndGet() == 0) {
|
||||
guard withGuard {
|
||||
futures remove invocation.uuid
|
||||
if (futures.isEmpty && uuids.isEmpty) {
|
||||
if (futures.get == 0 && uuids.isEmpty) {
|
||||
shutdownSchedule match {
|
||||
case UNSCHEDULED =>
|
||||
shutdownSchedule = SCHEDULED
|
||||
|
|
@ -103,8 +118,6 @@ trait MessageDispatcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
executeFuture(invocation)
|
||||
}
|
||||
|
||||
private[akka] def register(actorRef: ActorRef) {
|
||||
if (actorRef.mailbox eq null)
|
||||
|
|
@ -121,7 +134,7 @@ trait MessageDispatcher {
|
|||
private[akka] def unregister(actorRef: ActorRef) = {
|
||||
if (uuids remove actorRef.uuid) {
|
||||
actorRef.mailbox = null
|
||||
if (uuids.isEmpty && futures.isEmpty){
|
||||
if (uuids.isEmpty && futures.get == 0){
|
||||
shutdownSchedule match {
|
||||
case UNSCHEDULED =>
|
||||
shutdownSchedule = SCHEDULED
|
||||
|
|
@ -155,7 +168,7 @@ trait MessageDispatcher {
|
|||
shutdownSchedule = SCHEDULED
|
||||
Scheduler.scheduleOnce(this, timeoutMs, TimeUnit.MILLISECONDS)
|
||||
case SCHEDULED =>
|
||||
if (uuids.isEmpty() && futures.isEmpty) {
|
||||
if (uuids.isEmpty && futures.get == 0) {
|
||||
active switchOff {
|
||||
shutdown // shut down in the dispatcher's references is zero
|
||||
}
|
||||
|
|
@ -187,17 +200,17 @@ trait MessageDispatcher {
|
|||
*/
|
||||
private[akka] def dispatch(invocation: MessageInvocation): Unit
|
||||
|
||||
private[akka] def executeFuture(invocation: FutureInvocation): Unit
|
||||
private[akka] def executeFuture(invocation: FutureInvocation[_]): Unit
|
||||
|
||||
/**
|
||||
* Called one time every time an actor is attached to this dispatcher and this dispatcher was previously shutdown
|
||||
*/
|
||||
private[akka] def start: Unit
|
||||
private[akka] def start(): Unit
|
||||
|
||||
/**
|
||||
* Called one time every time an actor is detached from this dispatcher and this dispatcher has no actors left attached
|
||||
*/
|
||||
private[akka] def shutdown: Unit
|
||||
private[akka] def shutdown(): Unit
|
||||
|
||||
/**
|
||||
* Returns the size of the mailbox for the specified actor
|
||||
|
|
@ -205,9 +218,9 @@ trait MessageDispatcher {
|
|||
def mailboxSize(actorRef: ActorRef): Int
|
||||
|
||||
/**
|
||||
* Returns the size of the Future queue
|
||||
* Returns the amount of futures queued for execution
|
||||
*/
|
||||
def futureQueueSize: Int = futures.size
|
||||
def pendingFutures: Long = futures.get
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -221,9 +234,8 @@ abstract class MessageDispatcherConfigurator {
|
|||
|
||||
def mailboxType(config: Configuration): MailboxType = {
|
||||
val capacity = config.getInt("mailbox-capacity", Dispatchers.MAILBOX_CAPACITY)
|
||||
// FIXME how do we read in isBlocking for mailbox? Now set to 'false'.
|
||||
if (capacity < 1) UnboundedMailbox()
|
||||
else BoundedMailbox(false, capacity, Duration(config.getInt("mailbox-push-timeout-time", Dispatchers.MAILBOX_PUSH_TIME_OUT.toMillis.toInt), TIME_UNIT))
|
||||
else BoundedMailbox(capacity, Duration(config.getInt("mailbox-push-timeout-time", Dispatchers.MAILBOX_PUSH_TIME_OUT.toMillis.toInt), TIME_UNIT))
|
||||
}
|
||||
|
||||
def configureThreadPool(config: Configuration, createDispatcher: => (ThreadPoolConfig) => MessageDispatcher): ThreadPoolConfigDispatcherBuilder = {
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ class ThreadBasedDispatcher(_actor: ActorRef, _mailboxType: MailboxType)
|
|||
private[akka] val owner = new AtomicReference[ActorRef](_actor)
|
||||
|
||||
def this(actor: ActorRef) =
|
||||
this(actor, UnboundedMailbox(true)) // For Java API
|
||||
this(actor, UnboundedMailbox()) // For Java API
|
||||
|
||||
def this(actor: ActorRef, capacity: Int) =
|
||||
this(actor, BoundedMailbox(true, capacity)) //For Java API
|
||||
this(actor, BoundedMailbox(capacity)) //For Java API
|
||||
|
||||
def this(actor: ActorRef, capacity: Int, pushTimeOut: Duration) = //For Java API
|
||||
this(actor, BoundedMailbox(true, capacity, pushTimeOut))
|
||||
this(actor, BoundedMailbox(capacity, pushTimeOut))
|
||||
|
||||
override def register(actorRef: ActorRef) = {
|
||||
val actor = owner.get()
|
||||
|
|
|
|||
|
|
@ -221,9 +221,9 @@ trait ExecutorServiceDelegate extends ExecutorService {
|
|||
|
||||
def execute(command: Runnable) = executor.execute(command)
|
||||
|
||||
def shutdown = executor.shutdown
|
||||
def shutdown() { executor.shutdown() }
|
||||
|
||||
def shutdownNow = executor.shutdownNow
|
||||
def shutdownNow() = executor.shutdownNow()
|
||||
|
||||
def isShutdown = executor.isShutdown
|
||||
|
||||
|
|
|
|||
|
|
@ -102,9 +102,9 @@ object EventHandler extends ListenerManagement {
|
|||
/**
|
||||
* Shuts down all event handler listeners including the event handle dispatcher.
|
||||
*/
|
||||
def shutdown() = {
|
||||
foreachListener(_.stop)
|
||||
EventHandlerDispatcher.shutdown
|
||||
def shutdown() {
|
||||
foreachListener(_.stop())
|
||||
EventHandlerDispatcher.shutdown()
|
||||
}
|
||||
|
||||
def notify(event: Any) {
|
||||
|
|
@ -220,14 +220,15 @@ object EventHandler extends ListenerManagement {
|
|||
}
|
||||
defaultListeners foreach { listenerName =>
|
||||
try {
|
||||
ReflectiveAccess.getClassFor[Actor](listenerName) map { clazz =>
|
||||
addListener(Actor.actorOf(clazz).start())
|
||||
ReflectiveAccess.getClassFor[Actor](listenerName) match {
|
||||
case r: Right[_, Class[Actor]] => addListener(Actor.actorOf(r.b).start())
|
||||
case l: Left[Exception,_] => throw l.a
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
throw new ConfigurationException(
|
||||
"Event Handler specified in config can't be loaded [" + listenerName +
|
||||
"] due to [" + e.toString + "]")
|
||||
"] due to [" + e.toString + "]", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ case class RemoteServerWriteFailed(
|
|||
class RemoteClientException private[akka] (
|
||||
message: String,
|
||||
@BeanProperty val client: RemoteClientModule,
|
||||
val remoteAddress: InetSocketAddress) extends AkkaException(message)
|
||||
val remoteAddress: InetSocketAddress, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
||||
/**
|
||||
* Thrown when the remote server actor dispatching fails for some reason.
|
||||
|
|
@ -143,11 +143,11 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule
|
|||
handler
|
||||
}
|
||||
|
||||
def shutdown {
|
||||
def shutdown() {
|
||||
eventHandler.stop()
|
||||
removeListener(eventHandler)
|
||||
this.shutdownClientModule
|
||||
this.shutdownServerModule
|
||||
this.shutdownClientModule()
|
||||
this.shutdownServerModule()
|
||||
clear
|
||||
}
|
||||
|
||||
|
|
@ -189,13 +189,14 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule
|
|||
def actorOf(clazz: Class[_ <: Actor], host: String, port: Int): ActorRef = {
|
||||
import ReflectiveAccess.{ createInstance, noParams, noArgs }
|
||||
clientManagedActorOf(() =>
|
||||
createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs).getOrElse(
|
||||
throw new ActorInitializationException(
|
||||
"Could not instantiate Actor" +
|
||||
"\nMake sure Actor is NOT defined inside a class/trait," +
|
||||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")),
|
||||
host, port)
|
||||
createInstance[Actor](clazz.asInstanceOf[Class[_]], noParams, noArgs) match {
|
||||
case r: Right[_, Actor] => r.b
|
||||
case l: Left[Exception, _] => throw new ActorInitializationException(
|
||||
"Could not instantiate Actor" +
|
||||
"\nMake sure Actor is NOT defined inside a class/trait," +
|
||||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a)
|
||||
}, host, port)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -217,13 +218,14 @@ abstract class RemoteSupport extends ListenerManagement with RemoteServerModule
|
|||
def actorOf[T <: Actor : Manifest](host: String, port: Int): ActorRef = {
|
||||
import ReflectiveAccess.{ createInstance, noParams, noArgs }
|
||||
clientManagedActorOf(() =>
|
||||
createInstance[Actor](manifest[T].erasure.asInstanceOf[Class[_]], noParams, noArgs).getOrElse(
|
||||
throw new ActorInitializationException(
|
||||
"Could not instantiate Actor" +
|
||||
"\nMake sure Actor is NOT defined inside a class/trait," +
|
||||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.")),
|
||||
host, port)
|
||||
createInstance[Actor](manifest[T].erasure.asInstanceOf[Class[_]], noParams, noArgs) match {
|
||||
case r: Right[_, Actor] => r.b
|
||||
case l: Left[Exception, _] => throw new ActorInitializationException(
|
||||
"Could not instantiate Actor" +
|
||||
"\nMake sure Actor is NOT defined inside a class/trait," +
|
||||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a)
|
||||
}, host, port)
|
||||
}
|
||||
|
||||
protected override def manageLifeCycleOfListeners = false
|
||||
|
|
@ -354,7 +356,8 @@ trait RemoteServerModule extends RemoteModule {
|
|||
def registerByUuid(actorRef: ActorRef): Unit
|
||||
|
||||
/**
|
||||
* Register Remote Actor by a specific 'id' passed as argument.
|
||||
* Register Remote Actor by a specific 'id' passed as argument. The actor is registered by UUID rather than ID
|
||||
* when prefixing the handle with the “uuid:” protocol.
|
||||
* <p/>
|
||||
* NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ trait DefaultActorPool extends ActorPool { this: Actor =>
|
|||
private var _lastCapacityChange = 0
|
||||
private var _lastSelectorCount = 0
|
||||
|
||||
override def postStop = _delegates foreach {
|
||||
override def postStop() = _delegates foreach {
|
||||
delegate => try {
|
||||
delegate ! PoisonPill
|
||||
} catch { case e: Exception => } //Ignore any exceptions here
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class AkkaLoader {
|
|||
* Boot initializes the specified bundles
|
||||
*/
|
||||
def boot(withBanner: Boolean, b : Bootable): Unit = hasBooted switchOn {
|
||||
if (withBanner) printBanner
|
||||
if (withBanner) printBanner()
|
||||
println("Starting Akka...")
|
||||
b.onLoad
|
||||
Thread.currentThread.setContextClassLoader(getClass.getClassLoader)
|
||||
|
|
@ -32,15 +32,17 @@ class AkkaLoader {
|
|||
/*
|
||||
* Shutdown, well, shuts down the bundles used in boot
|
||||
*/
|
||||
def shutdown: Unit = hasBooted switchOff {
|
||||
println("Shutting down Akka...")
|
||||
_bundles.foreach(_.onUnload)
|
||||
_bundles = None
|
||||
Actor.shutdownHook.run
|
||||
println("Akka succesfully shut down")
|
||||
def shutdown() {
|
||||
hasBooted switchOff {
|
||||
println("Shutting down Akka...")
|
||||
_bundles.foreach(_.onUnload)
|
||||
_bundles = None
|
||||
Actor.shutdownHook.run
|
||||
println("Akka succesfully shut down")
|
||||
}
|
||||
}
|
||||
|
||||
private def printBanner = {
|
||||
private def printBanner() {
|
||||
println("==================================================")
|
||||
println(" t")
|
||||
println(" t t t")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
package akka.util
|
||||
|
||||
trait Bootable {
|
||||
def onLoad {}
|
||||
def onUnload {}
|
||||
def onLoad() {}
|
||||
def onUnload() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,16 @@ object ReflectiveAccess {
|
|||
lazy val isEnabled = remoteSupportClass.isDefined
|
||||
|
||||
def ensureEnabled = if (!isEnabled) {
|
||||
val e = new ModuleNotAvailableException(
|
||||
"Can't load the remoting module, make sure that akka-remote.jar is on the classpath")
|
||||
val e = new ModuleNotAvailableException("Can't load the remoting module, make sure that akka-remote.jar is on the classpath")
|
||||
EventHandler.debug(this, e.toString)
|
||||
throw e
|
||||
}
|
||||
val remoteSupportClass: Option[Class[_ <: RemoteSupport]] = getClassFor(TRANSPORT)
|
||||
val remoteSupportClass = getClassFor[RemoteSupport](TRANSPORT) match {
|
||||
case Right(value) => Some(value)
|
||||
case Left(exception) =>
|
||||
EventHandler.debug(this, exception.toString)
|
||||
None
|
||||
}
|
||||
|
||||
protected[akka] val defaultRemoteSupport: Option[() => RemoteSupport] =
|
||||
remoteSupportClass map { remoteClass =>
|
||||
|
|
@ -55,9 +59,11 @@ object ReflectiveAccess {
|
|||
remoteClass,
|
||||
Array[Class[_]](),
|
||||
Array[AnyRef]()
|
||||
) getOrElse {
|
||||
) match {
|
||||
case Right(value) => value
|
||||
case Left(exception) =>
|
||||
val e = new ModuleNotAvailableException(
|
||||
"Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName))
|
||||
"Can't instantiate [%s] - make sure that akka-remote.jar is on the classpath".format(remoteClass.getName), exception)
|
||||
EventHandler.debug(this, e.toString)
|
||||
throw e
|
||||
}
|
||||
|
|
@ -85,7 +91,12 @@ object ReflectiveAccess {
|
|||
"Can't load the typed actor module, make sure that akka-typed-actor.jar is on the classpath")
|
||||
|
||||
val typedActorObjectInstance: Option[TypedActorObject] =
|
||||
getObjectFor("akka.actor.TypedActor$")
|
||||
getObjectFor[TypedActorObject]("akka.actor.TypedActor$") match {
|
||||
case Right(value) => Some(value)
|
||||
case Left(exception)=>
|
||||
EventHandler.debug(this, exception.toString)
|
||||
None
|
||||
}
|
||||
|
||||
def resolveFutureIfMessageIsJoinPoint(message: Any, future: Future[_]): Boolean = {
|
||||
ensureEnabled
|
||||
|
|
@ -111,10 +122,20 @@ object ReflectiveAccess {
|
|||
lazy val isEnabled = clusterObjectInstance.isDefined
|
||||
|
||||
val clusterObjectInstance: Option[AnyRef] =
|
||||
getObjectFor("akka.cloud.cluster.Cluster$")
|
||||
getObjectFor[AnyRef]("akka.cloud.cluster.Cluster$") match {
|
||||
case Right(value) => Some(value)
|
||||
case Left(exception) =>
|
||||
EventHandler.debug(this, exception.toString)
|
||||
None
|
||||
}
|
||||
|
||||
val serializerClass: Option[Class[_]] =
|
||||
getClassFor("akka.serialization.Serializer")
|
||||
getClassFor("akka.serialization.Serializer") match {
|
||||
case Right(value) => Some(value)
|
||||
case Left(exception) =>
|
||||
EventHandler.debug(this, exception.toString)
|
||||
None
|
||||
}
|
||||
|
||||
def ensureEnabled = if (!isEnabled) throw new ModuleNotAvailableException(
|
||||
"Feature is only available in Akka Cloud")
|
||||
|
|
@ -125,91 +146,88 @@ object ReflectiveAccess {
|
|||
|
||||
def createInstance[T](clazz: Class[_],
|
||||
params: Array[Class[_]],
|
||||
args: Array[AnyRef]): Option[T] = try {
|
||||
args: Array[AnyRef]): Either[Exception,T] = try {
|
||||
assert(clazz ne null)
|
||||
assert(params ne null)
|
||||
assert(args ne null)
|
||||
val ctor = clazz.getDeclaredConstructor(params: _*)
|
||||
ctor.setAccessible(true)
|
||||
Some(ctor.newInstance(args: _*).asInstanceOf[T])
|
||||
Right(ctor.newInstance(args: _*).asInstanceOf[T])
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
EventHandler.debug(this, e.toString)
|
||||
None
|
||||
case e: Exception => Left(e)
|
||||
}
|
||||
|
||||
def createInstance[T](fqn: String,
|
||||
params: Array[Class[_]],
|
||||
args: Array[AnyRef],
|
||||
classloader: ClassLoader = loader): Option[T] = try {
|
||||
classloader: ClassLoader = loader): Either[Exception,T] = try {
|
||||
assert(params ne null)
|
||||
assert(args ne null)
|
||||
getClassFor(fqn) match {
|
||||
case Some(clazz) =>
|
||||
val ctor = clazz.getDeclaredConstructor(params: _*)
|
||||
case Right(value) =>
|
||||
val ctor = value.getDeclaredConstructor(params: _*)
|
||||
ctor.setAccessible(true)
|
||||
Some(ctor.newInstance(args: _*).asInstanceOf[T])
|
||||
case None => None
|
||||
Right(ctor.newInstance(args: _*).asInstanceOf[T])
|
||||
case Left(exception) => Left(exception) //We could just cast this to Either[Exception, T] but it's ugly
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
EventHandler.debug(this, e.toString)
|
||||
None
|
||||
Left(e)
|
||||
}
|
||||
|
||||
def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Option[T] = try {//Obtains a reference to $MODULE$
|
||||
//Obtains a reference to fqn.MODULE$
|
||||
def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,T] = try {
|
||||
getClassFor(fqn) match {
|
||||
case Some(clazz) =>
|
||||
val instance = clazz.getDeclaredField("MODULE$")
|
||||
case Right(value) =>
|
||||
val instance = value.getDeclaredField("MODULE$")
|
||||
instance.setAccessible(true)
|
||||
Option(instance.get(null).asInstanceOf[T])
|
||||
case None => None
|
||||
val obj = instance.get(null)
|
||||
if (obj eq null) Left(new NullPointerException) else Right(obj.asInstanceOf[T])
|
||||
case Left(exception) => Left(exception) //We could just cast this to Either[Exception, T] but it's ugly
|
||||
}
|
||||
} catch {
|
||||
case e: ExceptionInInitializerError =>
|
||||
EventHandler.debug(this, e.toString)
|
||||
throw e
|
||||
case e: Exception =>
|
||||
Left(e)
|
||||
}
|
||||
|
||||
def getClassFor[T](fqn: String, classloader: ClassLoader = loader): Option[Class[T]] = {
|
||||
def getClassFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception,Class[T]] = try {
|
||||
assert(fqn ne null)
|
||||
|
||||
// First, use the specified CL
|
||||
val first = try {
|
||||
Option(classloader.loadClass(fqn).asInstanceOf[Class[T]])
|
||||
Right(classloader.loadClass(fqn).asInstanceOf[Class[T]])
|
||||
} catch {
|
||||
case c: ClassNotFoundException => None
|
||||
case c: ClassNotFoundException => Left(c)
|
||||
}
|
||||
|
||||
if (first.isDefined) first
|
||||
if (first.isRight) first
|
||||
else {
|
||||
// Second option is to use the ContextClassLoader
|
||||
val second = try {
|
||||
Option(Thread.currentThread.getContextClassLoader.loadClass(fqn).asInstanceOf[Class[T]])
|
||||
Right(Thread.currentThread.getContextClassLoader.loadClass(fqn).asInstanceOf[Class[T]])
|
||||
} catch {
|
||||
case c: ClassNotFoundException => None
|
||||
case c: ClassNotFoundException => Left(c)
|
||||
}
|
||||
|
||||
if (second.isDefined) second
|
||||
if (second.isRight) second
|
||||
else {
|
||||
val third = try {
|
||||
// Don't try to use "loader" if we got the default "classloader" parameter
|
||||
if (classloader ne loader) Option(loader.loadClass(fqn).asInstanceOf[Class[T]])
|
||||
else None
|
||||
if (classloader ne loader) Right(loader.loadClass(fqn).asInstanceOf[Class[T]]) else Left(null) //Horrid
|
||||
} catch {
|
||||
case c: ClassNotFoundException => None
|
||||
case c: ClassNotFoundException => Left(c)
|
||||
}
|
||||
|
||||
if (third.isDefined) third
|
||||
if (third.isRight) third
|
||||
else {
|
||||
// Last option is Class.forName
|
||||
try {
|
||||
Option(Class.forName(fqn).asInstanceOf[Class[T]])
|
||||
Right(Class.forName(fqn).asInstanceOf[Class[T]]) // Last option is Class.forName
|
||||
} catch {
|
||||
case c: ClassNotFoundException => None
|
||||
case c: ClassNotFoundException => Left(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case e: Exception => Left(e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ PAPEROPT_letter = -D latex_paper_size=letter
|
|||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
# Set python path to include local packages for pygments styles.
|
||||
PYTHONPATH += $(LOCALPACKAGES)
|
||||
ifneq (,$(PYTHONPATH))
|
||||
PYTHONPATH := $(PYTHONPATH):$(LOCALPACKAGES)
|
||||
else
|
||||
PYTHONPATH := $(LOCALPACKAGES)
|
||||
endif
|
||||
export PYTHONPATH
|
||||
|
||||
.PHONY: help clean pygments html singlehtml latex pdf
|
||||
|
|
@ -40,8 +44,11 @@ pygments:
|
|||
@echo "Custom pygments styles have been installed."
|
||||
@echo
|
||||
|
||||
html: pygments
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
$(LOCALPACKAGES):
|
||||
$(MAKE) pygments
|
||||
|
||||
html: $(LOCALPACKAGES)
|
||||
$(SPHINXBUILD) -a -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
#########
|
||||
Utilities
|
||||
#########
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
This section of the manual describes miscellaneous utilities which are provided
|
||||
by Akka and used in multiple places.
|
||||
|
||||
.. _Duration:
|
||||
|
||||
########
|
||||
Duration
|
||||
========
|
||||
########
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Durations are used throughout the Akka library, wherefore this concept is
|
||||
represented by a special data type, :class:`Duration`. Values of this type may
|
||||
represent infinite (:obj:`Duration.Inf`, :obj:`Duration.MinusInf`) or finite
|
||||
durations, where the latter are constructable using a mini-DSL:
|
||||
durations.
|
||||
|
||||
Scala
|
||||
=====
|
||||
|
||||
In Scala durations are constructable using a mini-DSL and support all expected operations:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -27,6 +24,8 @@ durations, where the latter are constructable using a mini-DSL:
|
|||
val threemillis = 3.millis
|
||||
val diff = fivesec - threemillis
|
||||
assert (diff < fivesec)
|
||||
val fourmillis = threemillis * 4 / 3 // though you cannot write it the other way around
|
||||
val n = threemillis / (1 millisecond)
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -35,6 +34,9 @@ durations, where the latter are constructable using a mini-DSL:
|
|||
if the time unit is the last token on a line, otherwise semi-colon inference
|
||||
might go wrong, depending on what starts the next line.
|
||||
|
||||
Java
|
||||
====
|
||||
|
||||
Java provides less syntactic sugar, so you have to spell out the operations as
|
||||
method calls instead:
|
||||
|
||||
8
akka-docs/common/index.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Common utilities
|
||||
==========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
scheduler
|
||||
duration
|
||||
23
akka-docs/common/scheduler.rst
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Scheduler
|
||||
=========
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
``Akka`` has a little scheduler written using actors.
|
||||
This can be convenient if you want to schedule some periodic task for maintenance or similar.
|
||||
|
||||
It allows you to register a message that you want to be sent to a specific actor at a periodic interval.
|
||||
|
||||
Here is an example:
|
||||
-------------------
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Scheduler
|
||||
|
||||
//Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages
|
||||
Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit)
|
||||
|
||||
//Sends messageToBeSent to receiverActor after delayUntilSend
|
||||
Scheduler.scheduleOnce(receiverActor, messageToBeSent, delayUntilSend, timeUnit)
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ extensions = ['sphinx.ext.todo', 'includecode']
|
|||
templates_path = ['_templates']
|
||||
source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
exclude_patterns = ['_build', 'pending']
|
||||
exclude_patterns = ['_build', 'pending', 'disabled']
|
||||
|
||||
project = u'Akka'
|
||||
copyright = u'2009-2011, Scalable Solutions AB'
|
||||
|
|
|
|||
|
|
@ -6,25 +6,25 @@ Code Style
|
|||
|
||||
The Akka code style follows `this document <http://davetron5000.github.com/scala-style/ScalaStyleGuide.pdf>`_ .
|
||||
|
||||
Here is a code style settings file for IntelliJ IDEA.
|
||||
`<file:akka-intellij-code-style.jar>`_
|
||||
Here is a code style settings file for ``IntelliJ IDEA``:
|
||||
`Download <http://scalablesolutions.se/akka/docs/akka-0.10/files/akka-intellij-code-style.jar>`_
|
||||
|
||||
Please follow the code style. Look at the code around you and mimic.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
All code that is checked in should have tests. All testing is done with ScalaTest and ScalaCheck.
|
||||
All code that is checked in **should** have tests. All testing is done with ``ScalaTest`` and ``ScalaCheck``.
|
||||
|
||||
* Name tests as *Test.scala if they do not depend on any external stuff. That keeps surefire happy.
|
||||
* Name tests as *Spec.scala if they have external dependencies.
|
||||
* Name tests as **Test.scala** if they do not depend on any external stuff. That keeps surefire happy.
|
||||
* Name tests as **Spec.scala** if they have external dependencies.
|
||||
|
||||
There is a testing standard that should be followed: `Ticket001Spec <@https://github.com/jboner/akka/blob/master/akka-actor/src/test/scala/akka/ticket/Ticket001Spec.scala>`_
|
||||
There is a testing standard that should be followed: `Ticket001Spec <https://github.com/jboner/akka/blob/master/akka-actor-tests/src/test/scala/akka/ticket/Ticket001Spec.scala>`_
|
||||
|
||||
Actor TestKit
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
There is a useful test kit for testing actors: `akka.util.TestKit <@https://github.com/jboner/akka/tree/master/akka-actor/src/main/scala/akka/util/TestKit.scala>`_. It enables assertions concerning replies received and their timing, there is more documentation in the `<TestKit>`_ module.
|
||||
There is a useful test kit for testing actors: `akka.util.TestKit <https://github.com/jboner/akka/tree/master/akka-testkit/src/main/scala/akka/testkit/TestKit.scala>`_. It enables assertions concerning replies received and their timing, there is more documentation in the `<TestKit>`_ module.
|
||||
|
||||
NetworkFailureTest
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -7,6 +7,10 @@
|
|||
Documentation
|
||||
###############
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
The Akka documentation uses `reStructuredText`_ as its markup language and is
|
||||
built using `Sphinx`_.
|
||||
|
||||
|
|
@ -67,3 +71,83 @@ For example::
|
|||
Here is a reference to "akka section": :ref:`akka-section` which will have the
|
||||
name "Akka Section".
|
||||
|
||||
Build the documentation
|
||||
=======================
|
||||
|
||||
First install `Sphinx`_. See below.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
cd akka-docs
|
||||
|
||||
make html
|
||||
open _build/html/index.html
|
||||
|
||||
make pdf
|
||||
open _build/latex/Akka.pdf
|
||||
|
||||
|
||||
Installing Sphinx on OS X
|
||||
-------------------------
|
||||
|
||||
Install `Homebrew <https://github.com/mxcl/homebrew>`_
|
||||
|
||||
Install Python and pip:
|
||||
|
||||
::
|
||||
|
||||
brew install python
|
||||
/usr/local/share/python/easy_install pip
|
||||
|
||||
Add the Homebrew Python path to your $PATH:
|
||||
|
||||
::
|
||||
|
||||
/usr/local/Cellar/python/2.7.1/bin
|
||||
|
||||
|
||||
More information in case of trouble:
|
||||
https://github.com/mxcl/homebrew/wiki/Homebrew-and-Python
|
||||
|
||||
Install sphinx:
|
||||
|
||||
::
|
||||
|
||||
pip install sphinx
|
||||
|
||||
Add sphinx_build to your $PATH:
|
||||
|
||||
::
|
||||
|
||||
/usr/local/share/python
|
||||
|
||||
Install BasicTeX package from:
|
||||
http://www.tug.org/mactex/morepackages.html
|
||||
|
||||
Add texlive bin to $PATH:
|
||||
|
||||
::
|
||||
|
||||
/usr/local/texlive/2010basic/bin/universal-darwin
|
||||
|
||||
Add missing tex packages:
|
||||
|
||||
::
|
||||
|
||||
sudo tlmgr update --self
|
||||
sudo tlmgr install titlesec
|
||||
sudo tlmgr install framed
|
||||
sudo tlmgr install threeparttable
|
||||
sudo tlmgr install wrapfig
|
||||
sudo tlmgr install helvetic
|
||||
sudo tlmgr install courier
|
||||
|
||||
Link the akka pygments style:
|
||||
|
||||
::
|
||||
|
||||
cd /usr/local/Cellar/python/2.7.1/lib/python2.7/site-packages/pygments/styles
|
||||
ln -s /path/to/akka/akka-docs/themes/akka/pygments/akka.py akka.py
|
||||
|
|
|
|||
|
|
@ -5,3 +5,6 @@ Information for Developers
|
|||
:maxdepth: 2
|
||||
|
||||
documentation
|
||||
developer-guidelines
|
||||
sponsors
|
||||
team
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
****<span style="font-size: 150%;">Sponsors </span>****
|
||||
=======================================================
|
||||
|
||||
Scalable Solutions
|
||||
==================
|
||||
|
||||
Scalable Solutions AB is the commercial entity behind Akka, providing support, consulting and training around Akka.
|
||||
`<http://scalablesolutions.se>`_
|
||||
Sponsors
|
||||
============
|
||||
|
||||
YourKit
|
||||
=======
|
||||
25
akka-docs/dev/team.rst
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Team
|
||||
=====
|
||||
|
||||
=================== ========================== =================================
|
||||
Name Role Email
|
||||
=================== ========================== =================================
|
||||
Jonas Bonér Founder, Despot, Committer jonas AT jonasboner DOT com
|
||||
Viktor Klang Bad cop, Committer viktor DOT klang AT gmail DOT com
|
||||
Debasish Ghosh Committer dghosh AT acm DOT org
|
||||
Ross McDonald Alumni rossajmcd AT gmail DOT com
|
||||
Eckhart Hertzler Alumni
|
||||
Mikael Högqvist Alumni
|
||||
Tim Perrett Alumni
|
||||
Jeanfrancois Arcand Alumni jfarcand AT apache DOT org
|
||||
Martin Krasser Committer krasserm AT googlemail DOT com
|
||||
Jan Van Besien Alumni
|
||||
Michael Kober Alumni
|
||||
Peter Vlugter Committer
|
||||
Peter Veentjer Committer
|
||||
Irmo Manie Committer
|
||||
Heiko Seeberger Committer
|
||||
Hiram Chirino Committer
|
||||
Scott Clasen Committer
|
||||
Roland Kuhn Committer
|
||||
=================== ========================== =================================
|
||||
|
|
@ -91,11 +91,11 @@ object Pi extends App {
|
|||
}
|
||||
//#master-receive
|
||||
|
||||
override def preStart {
|
||||
override def preStart() {
|
||||
start = now
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
println(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis"
|
||||
|
|
@ -19,14 +19,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus
|
|||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: pi-formula.png
|
||||
.. 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 <https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first>`_, with the actual source code `here <https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala>`_.
|
||||
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
|
||||
-------------
|
||||
173
akka-docs/general/building-akka.rst
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
|
||||
.. highlightlang:: none
|
||||
|
||||
.. _building-akka:
|
||||
|
||||
###############
|
||||
Building Akka
|
||||
###############
|
||||
|
||||
This page describes how to build and run Akka from the latest source code.
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
|
||||
Get the source code
|
||||
===================
|
||||
|
||||
Akka uses `Git`_ and is hosted at `Github`_.
|
||||
|
||||
.. _Git: http://git-scm.com
|
||||
.. _Github: http://github.com
|
||||
|
||||
You first need Git installed on your machine. You can then clone the source
|
||||
repositories:
|
||||
|
||||
- Akka repository from http://github.com/jboner/akka
|
||||
- Akka Modules repository from http://github.com/jboner/akka-modules
|
||||
|
||||
For example::
|
||||
|
||||
git clone git://github.com/jboner/akka.git
|
||||
git clone git://github.com/jboner/akka-modules.git
|
||||
|
||||
If you have already cloned the repositories previously then you can update the
|
||||
code with ``git pull``::
|
||||
|
||||
git pull origin master
|
||||
|
||||
|
||||
SBT - Simple Build Tool
|
||||
=======================
|
||||
|
||||
Akka is using the excellent `SBT`_ build system. So the first thing you have to
|
||||
do is to download and install SBT. You can read more about how to do that in the
|
||||
`SBT setup`_ documentation.
|
||||
|
||||
.. _SBT: http://code.google.com/p/simple-build-tool
|
||||
.. _SBT setup: http://code.google.com/p/simple-build-tool/wiki/Setup
|
||||
|
||||
The SBT commands that you'll need to build Akka are all included below. If you
|
||||
want to find out more about SBT and using it for your own projects do read the
|
||||
`SBT documentation`_.
|
||||
|
||||
.. _SBT documentation: http://code.google.com/p/simple-build-tool/wiki/RunningSbt
|
||||
|
||||
The Akka SBT build file is ``project/build/AkkaProject.scala`` with some
|
||||
properties defined in ``project/build.properties``.
|
||||
|
||||
|
||||
Building Akka
|
||||
=============
|
||||
|
||||
First make sure that you are in the akka code directory::
|
||||
|
||||
cd akka
|
||||
|
||||
|
||||
Fetching dependencies
|
||||
---------------------
|
||||
|
||||
SBT does not fetch dependencies automatically. You need to manually do this with
|
||||
the ``update`` command::
|
||||
|
||||
sbt update
|
||||
|
||||
Once finished, all the dependencies for Akka will be in the ``lib_managed``
|
||||
directory under each module: akka-actor, akka-stm, and so on.
|
||||
|
||||
*Note: you only need to run update the first time you are building the code,
|
||||
or when the dependencies have changed.*
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
To compile all the Akka core modules use the ``compile`` command::
|
||||
|
||||
sbt compile
|
||||
|
||||
You can run all tests with the ``test`` command::
|
||||
|
||||
sbt test
|
||||
|
||||
If compiling and testing are successful then you have everything working for the
|
||||
latest Akka development version.
|
||||
|
||||
|
||||
Publish to local Ivy repository
|
||||
-------------------------------
|
||||
|
||||
If you want to deploy the artifacts to your local Ivy repository (for example,
|
||||
to use from an SBT project) use the ``publish-local`` command::
|
||||
|
||||
sbt publish-local
|
||||
|
||||
|
||||
Publish to local Maven repository
|
||||
---------------------------------
|
||||
|
||||
If you want to deploy the artifacts to your local Maven repository use::
|
||||
|
||||
sbt publish-local publish
|
||||
|
||||
|
||||
SBT interactive mode
|
||||
--------------------
|
||||
|
||||
Note that in the examples above we are calling ``sbt compile`` and ``sbt test``
|
||||
and so on. SBT also has an interactive mode. If you just run ``sbt`` you enter
|
||||
the interactive SBT prompt and can enter the commands directly. This saves
|
||||
starting up a new JVM instance for each command and can be much faster and more
|
||||
convenient.
|
||||
|
||||
For example, building Akka as above is more commonly done like this::
|
||||
|
||||
% sbt
|
||||
[info] Building project akka 1.1-SNAPSHOT against Scala 2.9.0.RC1
|
||||
[info] using AkkaParentProject with sbt 0.7.6.RC0 and Scala 2.7.7
|
||||
> update
|
||||
[info]
|
||||
[info] == akka-actor / update ==
|
||||
...
|
||||
[success] Successful.
|
||||
[info]
|
||||
[info] Total time ...
|
||||
> compile
|
||||
...
|
||||
> test
|
||||
...
|
||||
|
||||
|
||||
SBT batch mode
|
||||
--------------
|
||||
|
||||
It's also possible to combine commands in a single call. For example, updating,
|
||||
testing, and publishing Akka to the local Ivy repository can be done with::
|
||||
|
||||
sbt update test publish-local
|
||||
|
||||
|
||||
Building Akka Modules
|
||||
=====================
|
||||
|
||||
See the Akka Modules documentation.
|
||||
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
If you are managing dependencies by hand you can find the dependencies for each
|
||||
module by looking in the ``lib_managed`` directories. For example, this will
|
||||
list all compile dependencies (providing you have the source code and have run
|
||||
``sbt update``)::
|
||||
|
||||
cd akka
|
||||
ls -1 */lib_managed/compile
|
||||
|
||||
You can also look at the Ivy dependency resolution information that is created
|
||||
on ``sbt update`` and found in ``~/.ivy2/cache``. For example, the
|
||||
``.ivy2/cache/se.scalablesolutions.akka-akka-remote-compile.xml`` file contains
|
||||
the resolution information for the akka-remote module compile dependencies. If
|
||||
you open this file in a web browser you will get an easy to navigate view of
|
||||
dependencies.
|
||||
103
akka-docs/general/configuration.rst
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Specifying the configuration file
|
||||
---------------------------------
|
||||
|
||||
If you don't specify a configuration file then Akka uses default values, corresponding to the ``akka-reference.conf``
|
||||
that you see below. You can specify your own configuration file to override any property in the reference config.
|
||||
You only have to define the properties that differ from the default configuration.
|
||||
|
||||
The location of the config file to use can be specified in various ways:
|
||||
|
||||
* Define the ``-Dakka.config=...`` system property parameter with a file path to configuration file.
|
||||
|
||||
* Put an ``akka.conf`` file in the root of the classpath.
|
||||
|
||||
* Define the ``AKKA_HOME`` environment variable pointing to the root of the Akka
|
||||
distribution. The config is taken from the ``AKKA_HOME/config/akka.conf``. You
|
||||
can also point to the AKKA_HOME by specifying the ``-Dakka.home=...`` system
|
||||
property parameter.
|
||||
|
||||
If several of these ways to specify the config file are used at the same time the precedence is the order as given above,
|
||||
i.e. you can always redefine the location with the ``-Dakka.config=...`` system property.
|
||||
|
||||
|
||||
Defining the configuration file
|
||||
-------------------------------
|
||||
|
||||
Here is the reference configuration file:
|
||||
|
||||
.. literalinclude:: ../../config/akka-reference.conf
|
||||
:language: none
|
||||
|
||||
A custom ``akka.conf`` might look like this:
|
||||
|
||||
::
|
||||
|
||||
# In this file you can override any option defined in the 'akka-reference.conf' file.
|
||||
# Copy in all or parts of the 'akka-reference.conf' file and modify as you please.
|
||||
|
||||
akka {
|
||||
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
|
||||
|
||||
# Comma separated list of the enabled modules.
|
||||
enabled-modules = ["camel", "remote"]
|
||||
|
||||
# These boot classes are loaded (and created) automatically when the Akka Microkernel boots up
|
||||
# Can be used to bootstrap your application(s)
|
||||
# Should be the FQN (Fully Qualified Name) of the boot class which needs to have a default constructor
|
||||
boot = ["sample.camel.Boot",
|
||||
"sample.myservice.Boot"]
|
||||
|
||||
actor {
|
||||
throughput = 10 # Throughput for ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness
|
||||
}
|
||||
|
||||
remote {
|
||||
server {
|
||||
port = 2562 # The port clients should connect to. Default is 2552 (AKKA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Specifying files for different modes
|
||||
------------------------------------
|
||||
|
||||
You can use different configuration files for different purposes by specifying a mode option, either as
|
||||
``-Dakka.mode=...`` system property or as ``AKKA_MODE=...`` environment variable. For example using DEBUG log level
|
||||
when in development mode. Run with ``-Dakka.mode=dev`` and place the following ``akka.dev.conf`` in the root of
|
||||
the classpath.
|
||||
|
||||
akka.dev.conf:
|
||||
|
||||
::
|
||||
|
||||
akka {
|
||||
event-handler-level = "DEBUG"
|
||||
}
|
||||
|
||||
The mode option works in the same way when using configuration files in ``AKKA_HOME/config/`` directory.
|
||||
|
||||
The mode option is not used when specifying the configuration file with ``-Dakka.config=...`` system property.
|
||||
|
||||
Including files
|
||||
---------------
|
||||
|
||||
Sometimes it can be useful to include another configuration file, for example if you have one ``akka.conf`` with all
|
||||
environment independent settings and then override some settings for specific modes.
|
||||
|
||||
akka.dev.conf:
|
||||
|
||||
::
|
||||
|
||||
include "akka.conf"
|
||||
|
||||
akka {
|
||||
event-handler-level = "DEBUG"
|
||||
}
|
||||
|
||||
|
|
@ -12,7 +12,8 @@ You can configure which event handlers should be registered at boot time. That i
|
|||
.. code-block:: ruby
|
||||
|
||||
akka {
|
||||
event-handlers = ["akka.event.EventHandler$DefaultListener"] # event handlers to register at boot time (EventHandler$DefaultListener logs to STDOUT)
|
||||
# event handlers to register at boot time (EventHandler$DefaultListener logs to STDOUT)
|
||||
event-handlers = ["akka.event.EventHandler$DefaultListener"]
|
||||
event-handler-level = "DEBUG" # Options: ERROR, WARNING, INFO, DEBUG
|
||||
}
|
||||
|
||||
|
|
@ -88,9 +89,10 @@ The methods take a call-by-name parameter for the message to avoid object alloca
|
|||
|
||||
From Java you need to nest the call in an if statement to achieve the same thing.
|
||||
|
||||
`<code format="scala">`_
|
||||
if (EventHandler.isDebugEnabled()) {
|
||||
EventHandler.debug(this, String.format("Processing took %s ms", duration));
|
||||
}
|
||||
.. code-block:: java
|
||||
|
||||
if (EventHandler.isDebugEnabled()) {
|
||||
EventHandler.debug(this, String.format("Processing took %s ms", duration));
|
||||
}
|
||||
|
||||
|
||||
`<code>`_
|
||||
|
|
@ -4,5 +4,10 @@ General
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
jmm
|
||||
migration-guides
|
||||
util
|
||||
building-akka
|
||||
configuration
|
||||
event-handler
|
||||
issue-tracking
|
||||
licenses
|
||||
|
|
|
|||
56
akka-docs/general/issue-tracking.rst
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
Issue Tracking
|
||||
==============
|
||||
|
||||
Akka is using ``Assembla`` as issue tracking system.
|
||||
|
||||
Browsing
|
||||
--------
|
||||
|
||||
Tickets
|
||||
^^^^^^^
|
||||
|
||||
`You can find the Akka tickets here <http://www.assembla.com/spaces/akka>`_
|
||||
|
||||
`You can find the Akka Modules tickets here <https://www.assembla.com/spaces/akka-modules/tickets>`_
|
||||
|
||||
Roadmaps
|
||||
^^^^^^^^
|
||||
|
||||
`The roadmap for each Akka milestone is here <https://www.assembla.com/spaces/akka/milestones>`_
|
||||
|
||||
`The roadmap for each Akka Modules milestone is here <https://www.assembla.com/spaces/akka-modules/milestones>`_
|
||||
|
||||
Creating tickets
|
||||
----------------
|
||||
|
||||
In order to create tickets you need to do the following:
|
||||
|
||||
`Register here <https://www.assembla.com/user/signup>`_ then log in
|
||||
|
||||
For Akka tickets:
|
||||
`Link to create new ticket <https://www.assembla.com/spaces/akka/tickets/new>`_
|
||||
|
||||
|
||||
For Akka Modules tickets:
|
||||
`Link to create new ticket <https://www.assembla.com/spaces/akka-modules/tickets>`_
|
||||
|
||||
Thanks a lot for reporting bugs and suggesting features.
|
||||
|
||||
Failing test
|
||||
------------
|
||||
|
||||
Please submit a failing test on the following format:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class Ticket001Spec extends WordSpec with MustMatchers {
|
||||
|
||||
"An XXX" should {
|
||||
"do YYY" in {
|
||||
1 must be (1)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
akka-docs/general/jmm.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
Akka and the Java Memory Model
|
||||
================================
|
||||
|
||||
Prior to Java 5, the Java Memory Model (JMM) was broken. It was possible to get all kinds of strange results like unpredictable merged writes made by concurrent executing threads, unexpected reordering of instructions, and even final fields were not guaranteed to be final. With Java 5 and JSR-133, the Java Memory Model is clearly specified. This specification makes it possible to write code that performs, but doesn't cause concurrency problems. The Java Memory Model is specified in 'happens before'-rules, e.g.:
|
||||
* **monitor lock rule**: a release of a lock happens before every subsequent acquire of the same lock.
|
||||
* **volatile variable rule**: a write of a volatile variable happens before every subsequent read of the same volatile variable
|
||||
|
||||
The 'happens before'-rules clearly specify which visibility guarantees are provided on memory and which re-orderings are allowed. Without these rules it would not be possible to write concurrent and performant code in Java.
|
||||
|
||||
Actors and the Java Memory Model
|
||||
--------------------------------
|
||||
|
||||
With the Actors implementation in Akka, there are 2 ways multiple threads can execute actions on shared memory over time:
|
||||
* if a message is send to an actor (e.g. by another actor). In most cases messages are immutable, but if that message is not a properly constructed immutable object, without happens before rules, the system still could be subject to instruction re-orderings and visibility problems (so a possible source of concurrency errors).
|
||||
* if an actor makes changes to its internal state in one 'receive' method and access that state while processing another message. With the actors model you don't get any guarantee that the same thread will be executing the same actor for different messages. Without a happens before relation between these actions, there could be another source of concurrency errors.
|
||||
|
||||
To solve the 2 problems above, Akka adds the following 2 'happens before'-rules to the JMM:
|
||||
* **the actor send rule**: where the send of the message to an actor happens before the receive of the **same** actor.
|
||||
* **the actor subsequent processing rule**: where processing of one message happens before processing of the next message by the **same** actor.
|
||||
|
||||
Both rules only apply for the same actor instance and are not valid if different actors are used.
|
||||
|
||||
STM and the Java Memory Model
|
||||
-----------------------------
|
||||
|
||||
The Akka STM also provides a happens before rule called:
|
||||
|
||||
* **the transaction rule**: a commit on a transaction happens before every subsequent start of a transaction where there is at least 1 shared reference.
|
||||
|
||||
How these rules are realized in Akka, is an implementation detail and can change over time (the exact details could even depend on the used configuration) but they will lift on the other JMM rules like the monitor lock rule or the volatile variable rule. Essentially this means that you, the Akka user, do not need to worry about adding synchronization to provide such a happens before relation, because it is the responsibility of Akka. So you have your hands free to deal with your problems and not that of the framework.
|
||||
|
||||
|
||||
|
||||
|
|
@ -38,4 +38,4 @@ Akka Remote
|
|||
Akka Testkit
|
||||
------------
|
||||
|
||||
The TestKit moved into the akka-testkit subproject and correspondingly into the :code:`akka.testkit` package.
|
||||
The TestKit moved into the akka-testkit subproject and correspondingly into the ``akka.testkit`` package.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
BIN
akka-docs/images/clojure-trees.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
|
@ -6,7 +6,9 @@ Contents
|
|||
|
||||
intro/index
|
||||
general/index
|
||||
common/index
|
||||
scala/index
|
||||
java/index
|
||||
dev/index
|
||||
|
||||
Links
|
||||
|
|
|
|||
|
|
@ -1,340 +0,0 @@
|
|||
Building Akka
|
||||
=============
|
||||
|
||||
This page describes how to build and run Akka from the latest source code.
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
|
||||
Get the source code
|
||||
-------------------
|
||||
|
||||
Akka uses `Git <http://git-scm.com>`_ and is hosted at `Github
|
||||
<http://github.com>`_.
|
||||
|
||||
You first need Git installed on your machine. You can then clone the source
|
||||
repositories:
|
||||
|
||||
- Akka repository from `<http://github.com/jboner/akka>`_
|
||||
- Akka Modules repository from `<http://github.com/jboner/akka-modules>`_
|
||||
|
||||
For example::
|
||||
|
||||
git clone git://github.com/jboner/akka.git
|
||||
git clone git://github.com/jboner/akka-modules.git
|
||||
|
||||
If you have already cloned the repositories previously then you can update the
|
||||
code with ``git pull``::
|
||||
|
||||
git pull origin master
|
||||
|
||||
|
||||
SBT - Simple Build Tool
|
||||
-----------------------
|
||||
|
||||
Akka is using the excellent `SBT <http://code.google.com/p/simple-build-tool>`_
|
||||
build system. So the first thing you have to do is to download and install
|
||||
SBT. You can read more about how to do that `here
|
||||
<http://code.google.com/p/simple-build-tool/wiki/Setup>`_ .
|
||||
|
||||
The SBT commands that you'll need to build Akka are all included below. If you
|
||||
want to find out more about SBT and using it for your own projects do read the
|
||||
`SBT documentation
|
||||
<http://code.google.com/p/simple-build-tool/wiki/RunningSbt>`_.
|
||||
|
||||
The Akka SBT build file is ``project/build/AkkaProject.scala`` with some
|
||||
properties defined in ``project/build.properties``.
|
||||
|
||||
|
||||
Building Akka
|
||||
-------------
|
||||
|
||||
First make sure that you are in the akka code directory::
|
||||
|
||||
cd akka
|
||||
|
||||
|
||||
Fetching dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SBT does not fetch dependencies automatically. You need to manually do this with
|
||||
the ``update`` command::
|
||||
|
||||
sbt update
|
||||
|
||||
Once finished, all the dependencies for Akka will be in the ``lib_managed``
|
||||
directory under each module: akka-actor, akka-stm, and so on.
|
||||
|
||||
*Note: you only need to run update the first time you are building the code,
|
||||
or when the dependencies have changed.*
|
||||
|
||||
|
||||
Building
|
||||
^^^^^^^^
|
||||
|
||||
To compile all the Akka core modules use the ``compile`` command::
|
||||
|
||||
sbt compile
|
||||
|
||||
You can run all tests with the ``test`` command::
|
||||
|
||||
sbt test
|
||||
|
||||
If compiling and testing are successful then you have everything working for the
|
||||
latest Akka development version.
|
||||
|
||||
|
||||
Publish to local Ivy repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to deploy the artifacts to your local Ivy repository (for example,
|
||||
to use from an SBT project) use the ``publish-local`` command::
|
||||
|
||||
sbt publish-local
|
||||
|
||||
|
||||
Publish to local Maven repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to deploy the artifacts to your local Maven repository use::
|
||||
|
||||
sbt publish-local publish
|
||||
|
||||
|
||||
SBT interactive mode
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Note that in the examples above we are calling ``sbt compile`` and ``sbt test``
|
||||
and so on. SBT also has an interactive mode. If you just run ``sbt`` you enter
|
||||
the interactive SBT prompt and can enter the commands directly. This saves
|
||||
starting up a new JVM instance for each command and can be much faster and more
|
||||
convenient.
|
||||
|
||||
For example, building Akka as above is more commonly done like this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
% sbt
|
||||
[info] Building project akka 1.1-SNAPSHOT against Scala 2.9.0.RC1
|
||||
[info] using AkkaParentProject with sbt 0.7.6.RC0 and Scala 2.7.7
|
||||
> update
|
||||
[info]
|
||||
[info] == akka-actor / update ==
|
||||
...
|
||||
[success] Successful.
|
||||
[info]
|
||||
[info] Total time ...
|
||||
> compile
|
||||
...
|
||||
> test
|
||||
...
|
||||
|
||||
|
||||
SBT batch mode
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
It's also possible to combine commands in a single call. For example, updating,
|
||||
testing, and publishing Akka to the local Ivy repository can be done with::
|
||||
|
||||
sbt update test publish-local
|
||||
|
||||
|
||||
Building Akka Modules
|
||||
---------------------
|
||||
|
||||
To build Akka Modules first build and publish Akka to your local Ivy repository
|
||||
as described above. Or using::
|
||||
|
||||
cd akka
|
||||
sbt update publish-local
|
||||
|
||||
Then you can build Akka Modules using the same steps as building Akka. First
|
||||
update to get all dependencies (including the Akka core modules), then compile,
|
||||
test, or publish-local as needed. For example::
|
||||
|
||||
cd akka-modules
|
||||
sbt update publish-local
|
||||
|
||||
|
||||
Microkernel distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To build the Akka Modules microkernel (the same as the Akka Modules distribution
|
||||
download) use the ``dist`` command::
|
||||
|
||||
sbt dist
|
||||
|
||||
The distribution zip can be found in the dist directory and is called
|
||||
``akka-modules-{version}.zip``.
|
||||
|
||||
To run the microkernel, unzip the zip file, change into the unzipped directory,
|
||||
set the ``AKKA_HOME`` environment variable, and run the main jar file. For
|
||||
example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
unzip dist/akka-modules-1.1-SNAPSHOT.zip
|
||||
cd akka-modules-1.1-SNAPSHOT
|
||||
export AKKA_HOME=`pwd`
|
||||
java -jar akka-modules-1.1-SNAPSHOT.jar
|
||||
|
||||
The microkernel will boot up and install the sample applications that reside in
|
||||
the distribution's ``deploy`` directory. You can deploy your own applications
|
||||
into the ``deploy`` directory as well.
|
||||
|
||||
|
||||
Scripts
|
||||
-------
|
||||
|
||||
Linux/Unix init script
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here is a Linux/Unix init script that can be very useful:
|
||||
|
||||
http://github.com/jboner/akka/blob/master/scripts/akka-init-script.sh
|
||||
|
||||
Copy and modify as needed.
|
||||
|
||||
|
||||
Simple startup shell script
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This little script might help a bit. Just make sure you have the Akka
|
||||
distribution in the '$AKKA_HOME/dist' directory and then invoke this script to
|
||||
start up the kernel. The distribution is created in the './dist' dir for you if
|
||||
you invoke 'sbt dist'.
|
||||
|
||||
http://github.com/jboner/akka/blob/master/scripts/run_akka.sh
|
||||
|
||||
Copy and modify as needed.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
If you are managing dependencies by hand you can find out what all the compile
|
||||
dependencies are for each module by looking in the ``lib_managed/compile``
|
||||
directories. For example, you can run this to create a listing of dependencies
|
||||
(providing you have the source code and have run ``sbt update``)::
|
||||
|
||||
cd akka
|
||||
ls -1 */lib_managed/compile
|
||||
|
||||
|
||||
Dependencies used by the Akka core modules
|
||||
------------------------------------------
|
||||
|
||||
akka-actor
|
||||
^^^^^^^^^^
|
||||
|
||||
* No dependencies
|
||||
|
||||
akka-stm
|
||||
^^^^^^^^
|
||||
|
||||
* Depends on akka-actor
|
||||
* multiverse-alpha-0.6.2.jar
|
||||
|
||||
akka-typed-actor
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-stm
|
||||
* aopalliance-1.0.jar
|
||||
* aspectwerkz-2.2.3.jar
|
||||
* guice-all-2.0.jar
|
||||
|
||||
akka-remote
|
||||
^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-typed-actor
|
||||
* commons-codec-1.4.jar
|
||||
* commons-io-2.0.1.jar
|
||||
* dispatch-json_2.8.1-0.7.8.jar
|
||||
* guice-all-2.0.jar
|
||||
* h2-lzf-1.0.jar
|
||||
* jackson-core-asl-1.7.1.jar
|
||||
* jackson-mapper-asl-1.7.1.jar
|
||||
* junit-4.8.1.jar
|
||||
* netty-3.2.3.Final.jar
|
||||
* objenesis-1.2.jar
|
||||
* protobuf-java-2.3.0.jar
|
||||
* sjson_2.8.1-0.9.1.jar
|
||||
|
||||
akka-http
|
||||
^^^^^^^^^
|
||||
|
||||
* Depends on akka-remote
|
||||
* jsr250-api-1.0.jar
|
||||
* jsr311-api-1.1.jar
|
||||
|
||||
|
||||
Dependencies used by the Akka modules
|
||||
-------------------------------------
|
||||
|
||||
akka-amqp
|
||||
^^^^^^^^^
|
||||
|
||||
* Depends on akka-remote
|
||||
* commons-cli-1.1.jar
|
||||
* amqp-client-1.8.1.jar
|
||||
|
||||
akka-camel
|
||||
^^^^^^^^^^
|
||||
|
||||
* Depends on akka-actor
|
||||
* camel-core-2.7.0.jar
|
||||
* commons-logging-api-1.1.jar
|
||||
* commons-management-1.0.jar
|
||||
|
||||
akka-camel-typed
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-typed-actor
|
||||
* camel-core-2.7.0.jar
|
||||
* commons-logging-api-1.1.jar
|
||||
* commons-management-1.0.jar
|
||||
|
||||
akka-spring
|
||||
^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-camel
|
||||
* akka-camel-typed
|
||||
* commons-logging-1.1.1.jar
|
||||
* spring-aop-3.0.4.RELEASE.jar
|
||||
* spring-asm-3.0.4.RELEASE.jar
|
||||
* spring-beans-3.0.4.RELEASE.jar
|
||||
* spring-context-3.0.4.RELEASE.jar
|
||||
* spring-core-3.0.4.RELEASE.jar
|
||||
* spring-expression-3.0.4.RELEASE.jar
|
||||
|
||||
akka-scalaz
|
||||
^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-actor
|
||||
* hawtdispatch-1.1.jar
|
||||
* hawtdispatch-scala-1.1.jar
|
||||
* scalaz-core_2.8.1-6.0-SNAPSHOT.jar
|
||||
|
||||
akka-kernel
|
||||
^^^^^^^^^^^
|
||||
|
||||
* Depends on akka-http, akka-amqp, and akka-spring
|
||||
* activation-1.1.jar
|
||||
* asm-3.1.jar
|
||||
* jaxb-api-2.1.jar
|
||||
* jaxb-impl-2.1.12.jar
|
||||
* jersey-core-1.3.jar
|
||||
* jersey-json-1.3.jar
|
||||
* jersey-scala-1.3.jar
|
||||
* jersey-server-1.3.jar
|
||||
* jettison-1.1.jar
|
||||
* jetty-continuation-7.1.6.v20100715.jar
|
||||
* jetty-http-7.1.6.v20100715.jar
|
||||
* jetty-io-7.1.6.v20100715.jar
|
||||
* jetty-security-7.1.6.v20100715.jar
|
||||
* jetty-server-7.1.6.v20100715.jar
|
||||
* jetty-servlet-7.1.6.v20100715.jar
|
||||
* jetty-util-7.1.6.v20100715.jar
|
||||
* jetty-xml-7.1.6.v20100715.jar
|
||||
* servlet-api-2.5.jar
|
||||
* stax-api-1.0.1.jar
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
Specifying the configuration file
|
||||
---------------------------------
|
||||
|
||||
If you don't specify a configuration file then Akka uses default values. If
|
||||
you want to override these then you should edit the ``akka.conf`` file in the
|
||||
``AKKA_HOME/config`` directory. This config inherits from the
|
||||
``akka-reference.conf`` file that you see below. Use your ``akka.conf`` to override
|
||||
any property in the reference config.
|
||||
|
||||
The config can be specified in various ways:
|
||||
|
||||
* Define the ``-Dakka.config=...`` system property option
|
||||
|
||||
* Put an ``akka.conf`` file on the classpath
|
||||
|
||||
* Define the ``AKKA_HOME`` environment variable pointing to the root of the Akka
|
||||
distribution. The config is taken from the ``AKKA_HOME/config`` directory. You
|
||||
can also point to the AKKA_HOME by specifying the ``-Dakka.home=...`` system
|
||||
property option.
|
||||
|
||||
|
||||
Defining the configuration file
|
||||
-------------------------------
|
||||
|
||||
Here is the reference configuration file:
|
||||
|
||||
.. literalinclude:: ../../config/akka-reference.conf
|
||||
:language: none
|
||||
|
|
@ -19,14 +19,25 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus
|
|||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: pi-formula.png
|
||||
.. 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 a Maven 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 <https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first>`_, with the actual source code `here <https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java>`_.
|
||||
If you want don't want to type in the code and/or set up a Maven 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/java/akka/tutorial/first/java/Pi.java
|
||||
|
||||
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::
|
||||
|
||||
$ cd akka/akka-tutorials/akka-tutorial-first
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _getting-started-first-scala-eclipse:
|
||||
|
||||
Getting Started Tutorial (Scala with Eclipse): First Chapter
|
||||
============================================================
|
||||
|
||||
|
|
@ -12,14 +14,17 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus
|
|||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: pi-formula.png
|
||||
.. 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 <https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first>`_, with the actual source code `here <https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala>`_.
|
||||
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
|
||||
-------------
|
||||
|
|
@ -99,19 +104,19 @@ If you want to use Eclipse for coding your Akka tutorial, you need to install th
|
|||
|
||||
You can install this plugin using the regular update mechanism. First choose a version of the IDE from `http://download.scala-ide.org <http://download.scala-ide.org>`_. We recommend you choose 2.0.x, which comes with Scala 2.9. Copy the corresponding URL and then choose ``Help/Install New Software`` and paste the URL you just copied. You should see something similar to the following image.
|
||||
|
||||
.. image:: install-beta2-updatesite.png
|
||||
.. image:: ../images/install-beta2-updatesite.png
|
||||
|
||||
Make sure you select both the ``JDT Weaving for Scala`` and the ``Scala IDE for Eclipse`` plugins. The other plugin is optional, and contains the source code of the plugin itself.
|
||||
|
||||
Once the installation is finished, you need to restart Eclipse. The first time the plugin starts it will open a diagnostics window and offer to fix several settings, such as the delay for content assist (code-completion) or the shown completion proposal types.
|
||||
|
||||
.. image:: diagnostics-window.png
|
||||
.. image:: ../images/diagnostics-window.png
|
||||
|
||||
Accept the recommended settings, and follow the instructions if you need to increase the heap size of Eclipse.
|
||||
|
||||
Check that the installation succeeded by creating a new Scala project (``File/New>Scala Project``), and typing some code. You should have content-assist, hyperlinking to definitions, instant error reporting, and so on.
|
||||
|
||||
.. image:: example-code.png
|
||||
.. image:: ../images/example-code.png
|
||||
|
||||
You are ready to code now!
|
||||
|
||||
|
|
@ -140,7 +145,7 @@ Creating an Akka project in Eclipse
|
|||
|
||||
If you have not already done so, now is the time to create an Eclipse project for our tutorial. Use the ``New Scala Project`` wizard and accept the default settings. Once the project is open, we need to add the akka libraries to the *build path*. Right click on the project and choose ``Properties``, then click on ``Java Build Path``. Go to ``Libraries`` and click on ``Add External Jars..``, then navigate to the location where you installed akka and choose ``akka-actor.jar``. You should see something similar to this:
|
||||
|
||||
.. image:: build-path.png
|
||||
.. image:: ../images/build-path.png
|
||||
|
||||
Using SBT in Eclipse
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -186,7 +191,7 @@ Then run the ``eclipse`` target to generate the Eclipse project::
|
|||
|
||||
Next you need to import this project in Eclipse, by choosing ``Eclipse/Import.. Existing Projects into Workspace``. Navigate to the directory where you defined your SBT project and choose import:
|
||||
|
||||
.. image:: import-project.png
|
||||
.. image:: ../images/import-project.png
|
||||
|
||||
Now we have the basis for an Akka Eclipse application, so we can..
|
||||
|
||||
|
|
@ -234,7 +239,7 @@ Now we can create the worker actor. Create a new class called ``Worker`` as bef
|
|||
|
||||
The ``Actor`` trait is defined in ``akka.actor`` and you can either import it explicitly, or let Eclipse do it for you when it cannot resolve the ``Actor`` trait. The quick fix option (``Ctrl-F1``) will offer two options:
|
||||
|
||||
.. image:: quickfix.png
|
||||
.. image:: ../images/quickfix.png
|
||||
|
||||
Choose the Akka Actor and move on.
|
||||
|
||||
|
|
@ -307,11 +312,11 @@ Here is the master actor::
|
|||
|
||||
def receive = { ... }
|
||||
|
||||
override def preStart {
|
||||
override def preStart() {
|
||||
start = System.currentTimeMillis
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
println(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis"
|
||||
|
|
@ -403,7 +408,7 @@ If you have not defined an the ``AKKA_HOME`` environment variable then Akka can'
|
|||
|
||||
You can also define a new Run configuration, by going to ``Run/Run Configurations``. Create a new ``Scala application`` and choose the tutorial project and the main class to be ``akkatutorial.Pi``. You can pass additional command line arguments to the JVM on the ``Arguments`` page, for instance to define where ``akka.conf`` is:
|
||||
|
||||
.. image:: run-config.png
|
||||
.. image:: ../images/run-config.png
|
||||
|
||||
Once you finished your run configuration, click ``Run``. You should see the same output in the ``Console`` window. You can use the same configuration for debugging the application, by choosing ``Run/Debug History`` or just ``Debug As``.
|
||||
|
||||
|
|
|
|||
|
|
@ -19,14 +19,25 @@ We will be using an algorithm that is called "embarrassingly parallel" which jus
|
|||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: pi-formula.png
|
||||
.. 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 <https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first>`_, with the actual source code `here <https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala>`_.
|
||||
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
|
||||
|
||||
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::
|
||||
|
||||
$ cd akka/akka-tutorials/akka-tutorial-first
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
|
@ -173,8 +184,11 @@ Not needed in this tutorial, but if you would like to use additional Akka module
|
|||
|
||||
So, now we are all set. Just one final thing to do; make SBT download the dependencies it needs. That is done by invoking::
|
||||
|
||||
> reload
|
||||
> update
|
||||
|
||||
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.
|
||||
|
||||
Start writing the code
|
||||
|
|
@ -291,11 +305,11 @@ Here is the master actor::
|
|||
|
||||
def receive = { ... }
|
||||
|
||||
override def preStart {
|
||||
override def preStart() {
|
||||
start = System.currentTimeMillis
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
println(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis"
|
||||
|
|
@ -451,11 +465,11 @@ But before we package it up and run it, let's take a look at the full code now,
|
|||
if (nrOfResults == nrOfMessages) self.stop()
|
||||
}
|
||||
|
||||
override def preStart {
|
||||
override def preStart() {
|
||||
start = System.currentTimeMillis
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
println(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis"
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ Introduction
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
what-is-akka
|
||||
why-akka
|
||||
getting-started-first-scala
|
||||
getting-started-first-scala-eclipse
|
||||
getting-started-first-java
|
||||
building-akka
|
||||
configuration
|
||||
|
|
|
|||
33
akka-docs/intro/what-is-akka.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
What is Akka?
|
||||
====
|
||||
|
||||
**Akka**
|
||||
^^^^^^
|
||||
|
||||
**Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors**
|
||||
|
||||
We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with ``Software Transactional Memory`` we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the ``Let it crash`` / ``Embrace failure`` model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the ``Apache 2 License``.
|
||||
|
||||
|
||||
Download from `<http://akka.io/downloads/>`_
|
||||
|
||||
**Akka implements a unique hybrid of:**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* `Actors <untyped-actors-java>`_, which gives you:
|
||||
* Simple and high-level abstractions for concurrency and parallelism.
|
||||
* Asynchronous, non-blocking and highly performant event-driven programming model.
|
||||
* Very lightweight event-driven processes (create ~6.5 million actors on 4GB RAM).
|
||||
* `Failure management <fault-tolerance-java>`_ through supervisor hierarchies with `let-it-crash <http://letitcrash.com>`_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal.
|
||||
* `Software Transactional Memory <stm-java>`_ (STM). (Distributed transactions coming soon).
|
||||
* `Transactors <transactors-java>`_: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback.
|
||||
* `Remote actors <remote-actors-java>`_: highly performant distributed actors with remote supervision and error management.
|
||||
* Java and Scala API.
|
||||
|
||||
**Akka can be used in two different ways:**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath.
|
||||
* As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules.
|
||||
|
||||
See the `Use-case and Deployment Scenarios <deployment-scenarios>`_ for details.
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
Dispatchers (Java)
|
||||
==================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
The Dispatcher is an important piece that allows you to configure the right semantics and parameters for optimal performance, throughput and scalability. Different Actors have different needs.
|
||||
|
|
@ -128,7 +132,7 @@ If you don't define a the 'throughput' option in the configuration file then the
|
|||
Browse the `ScalaDoc <scaladoc>`_ or look at the code for all the options available.
|
||||
|
||||
Priority event-based
|
||||
^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply
|
||||
a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended):
|
||||
|
|
@ -137,7 +141,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator:
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
package some.package;
|
||||
package some.pkg;
|
||||
|
||||
import akka.actor.*;
|
||||
import akka.dispatch.*;
|
||||
|
|
@ -249,13 +253,14 @@ For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingD
|
|||
For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor.
|
||||
Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout.
|
||||
|
||||
`<code format="java">`_
|
||||
class MyActor extends UntypedActor {
|
||||
public MyActor() {
|
||||
int mailboxCapacity = 100;
|
||||
Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS);
|
||||
getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext(), mailboxCapacity, pushTimeout));
|
||||
.. code-block:: java
|
||||
|
||||
class MyActor extends UntypedActor {
|
||||
public MyActor() {
|
||||
int mailboxCapacity = 100;
|
||||
Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS);
|
||||
getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext(), mailboxCapacity, pushTimeout));
|
||||
}
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
`<code>`_
|
||||
|
||||
14
akka-docs/java/index.rst
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Java API
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
untyped-actors
|
||||
typed-actors
|
||||
actor-registry
|
||||
stm
|
||||
transactors
|
||||
remote-actors
|
||||
serialization
|
||||
dispatchers
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
Remote Actors (Java)
|
||||
====================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Akka supports starting UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
Akka supports starting interacting with UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
|
||||
The usage is completely transparent both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message.
|
||||
The usage is completely transparent with local actors, both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message.
|
||||
|
||||
**WARNING**: For security reasons, do not run an Akka node with a Remote Actor port reachable by untrusted connections unless you have supplied a classloader that restricts access to the JVM.
|
||||
|
||||
|
|
@ -142,12 +146,6 @@ The default behavior is that the remote client will maintain a transaction log o
|
|||
|
||||
If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown.
|
||||
|
||||
You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried).
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
Object[] pending = Actors.remote().pendingMessages();
|
||||
|
||||
Running Remote Server in untrusted mode
|
||||
---------------------------------------
|
||||
|
||||
|
|
@ -253,21 +251,13 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen
|
|||
|
||||
The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash.
|
||||
|
||||
Remote Actors
|
||||
-------------
|
||||
|
||||
Akka has two types of remote actors:
|
||||
|
||||
* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server.
|
||||
* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc.
|
||||
|
||||
Client-managed Remote UntypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
The client creates the remote actor and "moves it" to the server.
|
||||
|
||||
When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
|
||||
Here is an example:
|
||||
|
|
@ -291,26 +281,31 @@ An UntypedActor can also start remote child Actors through one of the “spawn/l
|
|||
.. code-block:: java
|
||||
|
||||
...
|
||||
getContext().spawnRemote(MyActor.class, hostname, port);
|
||||
getContext().spawnRemote(MyActor.class, hostname, port, timeoutInMsForFutures);
|
||||
getContext().spawnLinkRemote(MyActor.class, hostname, port, timeoutInMsForFutures);
|
||||
...
|
||||
|
||||
Server-managed Remote UntypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------
|
||||
|
||||
Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote actors is really simple. 2 methods only:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.Actors;
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
class MyActor extends UntypedActor {
|
||||
public void onReceive(Object message) throws Exception {
|
||||
...
|
||||
}
|
||||
}
|
||||
Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class);
|
||||
Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class));
|
||||
|
||||
Actors created like this are automatically started.
|
||||
|
||||
|
|
@ -322,88 +317,6 @@ You can also register an actor by its UUID rather than ID or handle. This is don
|
|||
|
||||
server.unregister("uuid:" + actor.uuid);
|
||||
|
||||
Client side usage
|
||||
*****************
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
ActorRef actor = Actors.remote().actorFor("hello-service", "localhost", 2552);
|
||||
actor.sendOneWay("Hello");
|
||||
|
||||
There are many variations on the 'remote()#actorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = actorFor(className, hostname, port);
|
||||
... = actorFor(className, timeout, hostname, port);
|
||||
... = actorFor(uuid, className, hostname, port);
|
||||
... = actorFor(uuid, className, timeout, hostname, port);
|
||||
... // etc
|
||||
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
||||
Client-managed Remote TypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, , "localhost", 2552);
|
||||
|
||||
And if you want to specify the timeout:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552);
|
||||
|
||||
You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
new Component(
|
||||
Foo.class,
|
||||
FooImpl.class,
|
||||
new LifeCycle(new Permanent(), 1000),
|
||||
1000,
|
||||
new RemoteAddress("localhost", 2552))
|
||||
|
||||
Server-managed Remote TypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000);
|
||||
remote().registerTypedActor("user-service", typedActor);
|
||||
|
||||
Client side usage
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552);
|
||||
actor.registerUser(...);
|
||||
|
||||
There are variations on the 'remote()#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port);
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port);
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader);
|
||||
|
||||
Session bound server side setup
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -414,17 +327,19 @@ Session bound actors are useful if you need to keep state per session, e.g. user
|
|||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
import akka.japi.Creator;
|
||||
|
||||
class HelloWorldActor extends Actor {
|
||||
...
|
||||
}
|
||||
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
remote().registerPerSession("hello-service", new Creator[ActorRef]() {
|
||||
remote().registerPerSession("hello-service", new Creator<ActorRef>() {
|
||||
public ActorRef create() {
|
||||
return actorOf(HelloWorldActor.class);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Note that the second argument in registerPerSession is a Creator, it means that the create method will create a new ActorRef each invocation.
|
||||
It will be called to create an actor every time a session is established.
|
||||
|
|
@ -443,19 +358,22 @@ There are many variations on the 'remote()#actorFor' method. Here are some of th
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
... = actorFor(className, hostname, port);
|
||||
... = actorFor(className, timeout, hostname, port);
|
||||
... = actorFor(uuid, className, hostname, port);
|
||||
... = actorFor(uuid, className, timeout, hostname, port);
|
||||
... = remote().actorFor(className, hostname, port);
|
||||
... = remote().actorFor(className, timeout, hostname, port);
|
||||
... = remote().actorFor(uuid, className, hostname, port);
|
||||
... = remote().actorFor(uuid, className, timeout, hostname, port);
|
||||
... // etc
|
||||
|
||||
Automatic remote 'sender' reference management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
||||
Akka is automatically remote-enabling the sender Actor reference for you in order to allow the receiver to respond to the message using 'getContext().getSender().sendOneWay(msg);' or 'getContext().reply(msg);'. By default it is registering the sender reference in the remote server with the 'hostname' and 'port' from the akka.conf configuration file. The default is "localhost" and 2552 and if there is no remote server with this hostname and port then it creates and starts it.
|
||||
Automatic remote 'sender' reference management
|
||||
----------------------------------------------
|
||||
|
||||
The sender of a remote message will be reachable with a reply through the remote server on the node that the actor is residing, automatically.
|
||||
Please note that firewalled clients won't work right now. [2011-01-05]
|
||||
|
||||
Identifying remote actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
-------------------------
|
||||
|
||||
The 'id' field in the 'Actor' class is of importance since it is used as identifier for the remote actor. If you want to create a brand new actor every time you instantiate a remote actor then you have to set the 'id' field to a unique 'String' for each instance. If you want to reuse the same remote actor instance for each new remote actor (of the same class) you create then you don't have to do anything since the 'id' field by default is equal to the name of the actor class.
|
||||
|
||||
|
|
@ -463,18 +381,83 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.util.UUID;
|
||||
import akka.actor.UntypedActor;
|
||||
import com.eaio.uuid.UUID;
|
||||
|
||||
class MyActor extends UntypedActor {
|
||||
public MyActor() {
|
||||
getContext().setId(UUID.newUuid().toString());
|
||||
getContext().setId(new UUID().toString());
|
||||
}
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
Client-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO) TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, "localhost", 2552);
|
||||
|
||||
And if you want to specify the timeout:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552);
|
||||
|
||||
You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
new Component(
|
||||
Foo.class,
|
||||
FooImpl.class,
|
||||
new LifeCycle(new Permanent(), 1000),
|
||||
1000,
|
||||
new RemoteAddress("localhost", 2552))
|
||||
|
||||
Server-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000);
|
||||
remote().registerTypedActor("user-service", typedActor);
|
||||
|
||||
|
||||
Client side usage
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552);
|
||||
actor.registerUser(...);
|
||||
|
||||
There are variations on the 'remote()#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port);
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port);
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader);
|
||||
|
||||
Data Compression Configuration
|
||||
------------------------------
|
||||
|
||||
|
|
@ -493,44 +476,55 @@ You can configure it like this:
|
|||
}
|
||||
}
|
||||
|
||||
Code provisioning
|
||||
-----------------
|
||||
|
||||
Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes.
|
||||
This is something that will be addressed soon. Until then, sorry for the inconvenience.
|
||||
|
||||
Subscribe to Remote Client events
|
||||
---------------------------------
|
||||
|
||||
Akka has a subscription API for remote client events. You can register an Actor as a listener and this actor will have to be able to process these events:
|
||||
|
||||
RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
.. code-block:: java
|
||||
|
||||
class RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
|
||||
So a simple listener actor can look like this:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.remoteinterface.*;
|
||||
|
||||
class Listener extends UntypedActor {
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof RemoteClientError) {
|
||||
RemoteClientError event = (RemoteClientError)message;
|
||||
Exception cause = event.getCause();
|
||||
...
|
||||
RemoteClientError event = (RemoteClientError) message;
|
||||
Throwable cause = event.getCause();
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientConnected) {
|
||||
RemoteClientConnected event = (RemoteClientConnected)message;
|
||||
...
|
||||
RemoteClientConnected event = (RemoteClientConnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientDisconnected) {
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected)message;
|
||||
...
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientStarted) {
|
||||
RemoteClientStarted event = (RemoteClientStarted)message;
|
||||
...
|
||||
RemoteClientStarted event = (RemoteClientStarted) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientShutdown) {
|
||||
RemoteClientShutdown event = (RemoteClientShutdown)message;
|
||||
...
|
||||
RemoteClientShutdown event = (RemoteClientShutdown) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientWriteFailed) {
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed)message;
|
||||
...
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed) message;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -550,43 +544,45 @@ Subscribe to Remote Server events
|
|||
|
||||
Akka has a subscription API for the server events. You can register an Actor as a listener and this actor will have to be able to process these events:
|
||||
|
||||
RemoteServerStarted { RemoteServerModule server; }
|
||||
RemoteServerShutdown { RemoteServerModule server; }
|
||||
RemoteServerError { Throwable cause; RemoteServerModule server; }
|
||||
RemoteServerClientConnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerClientDisconnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerClientClosed { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
.. code-block:: java
|
||||
|
||||
class RemoteServerStarted { RemoteServerModule server; }
|
||||
class RemoteServerShutdown { RemoteServerModule server; }
|
||||
class RemoteServerError { Throwable cause; RemoteServerModule server; }
|
||||
class RemoteServerClientConnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerClientDisconnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerClientClosed { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
|
||||
So a simple listener actor can look like this:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.remoteinterface.*;
|
||||
|
||||
class Listener extends UntypedActor {
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof RemoteServerError) {
|
||||
RemoteServerError event = (RemoteServerError)message;
|
||||
Exception cause = event.getCause();
|
||||
...
|
||||
} else if (message instanceof RemoteServerStarted) {
|
||||
RemoteServerStarted event = (RemoteServerStarted)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerShutdown) {
|
||||
RemoteServerShutdown event = (RemoteServerShutdown)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientConnected) {
|
||||
RemoteServerClientConnected event = (RemoteServerClientConnected)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientDisconnected) {
|
||||
RemoteServerClientDisconnected event = (RemoteServerClientDisconnected)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientClosed) {
|
||||
RemoteServerClientClosed event = (RemoteServerClientClosed)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerWriteFailed) {
|
||||
RemoteServerWriteFailed event = (RemoteServerWriteFailed)message;
|
||||
...
|
||||
if (message instanceof RemoteClientError) {
|
||||
RemoteClientError event = (RemoteClientError) message;
|
||||
Throwable cause = event.getCause();
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientConnected) {
|
||||
RemoteClientConnected event = (RemoteClientConnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientDisconnected) {
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientStarted) {
|
||||
RemoteClientStarted event = (RemoteClientStarted) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientShutdown) {
|
||||
RemoteClientShutdown event = (RemoteClientShutdown) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientWriteFailed) {
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed) message;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -608,10 +604,27 @@ Message Serialization
|
|||
|
||||
All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization.
|
||||
|
||||
Read more about that in the `Serialization section <serialization-java>`_.
|
||||
Here is one example, but full documentation can be found in the :ref:`serialization-java`.
|
||||
|
||||
Code provisioning
|
||||
-----------------
|
||||
Protobuf
|
||||
^^^^^^^^
|
||||
|
||||
Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes.
|
||||
This is something that will be addressed soon. Until then, sorry for the inconvenience.
|
||||
Protobuf message specification needs to be compiled with 'protoc' compiler.
|
||||
|
||||
::
|
||||
|
||||
message ProtobufPOJO {
|
||||
required uint64 id = 1;
|
||||
required string name = 2;
|
||||
required bool status = 3;
|
||||
}
|
||||
|
||||
Using the generated message builder to send the message to a remote actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
actor.sendOneWay(ProtobufPOJO.newBuilder()
|
||||
.setId(11)
|
||||
.setStatus(true)
|
||||
.setName("Coltrane")
|
||||
.build());
|
||||
|
|
@ -1,38 +1,23 @@
|
|||
.. _serialization-java:
|
||||
|
||||
Serialization (Java)
|
||||
====================
|
||||
|
||||
Akka serialization module has been documented extensively under the Scala API section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly.
|
||||
.. sidebar:: Contents
|
||||
|
||||
Serialization of ActorRef
|
||||
=========================
|
||||
.. contents:: :local:
|
||||
|
||||
The following are the Java APIs for serialization of local ActorRefs:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
/**
|
||||
* Module for local actor serialization.
|
||||
*/
|
||||
object ActorSerialization {
|
||||
// wrapper for implicits to be used by Java
|
||||
def fromBinaryJ[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef =
|
||||
fromBinary(bytes)(format)
|
||||
|
||||
// wrapper for implicits to be used by Java
|
||||
def toBinaryJ[T <: Actor](a: ActorRef, format: Format[T], srlMailBox: Boolean = true): Array[Byte] =
|
||||
toBinary(a, srlMailBox)(format)
|
||||
}
|
||||
|
||||
The following steps describe the procedure for serializing an Actor and ActorRef.
|
||||
Akka serialization module has been documented extensively under the :ref:`serialization-scala` section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly.
|
||||
|
||||
Serialization of a Stateless Actor
|
||||
==================================
|
||||
----------------------------------
|
||||
|
||||
Step 1: Define the Actor
|
||||
------------------------
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
public class SerializationTestActor extends UntypedActor {
|
||||
public void onReceive(Object msg) {
|
||||
getContext().replySafe("got it!");
|
||||
|
|
@ -40,12 +25,13 @@ Step 1: Define the Actor
|
|||
}
|
||||
|
||||
Step 2: Define the typeclass instance for the actor
|
||||
---------------------------------------------------
|
||||
|
||||
Note how the generated Java classes are accessed using the $class based naming convention of the Scala compiler.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.StatelessActorFormat;
|
||||
|
||||
class SerializationTestActorFormat implements StatelessActorFormat<SerializationTestActor> {
|
||||
@Override
|
||||
public SerializationTestActor fromBinary(byte[] bytes, SerializationTestActor act) {
|
||||
|
|
@ -58,12 +44,20 @@ Note how the generated Java classes are accessed using the $class based naming c
|
|||
}
|
||||
}
|
||||
|
||||
**Step 3: Serialize and de-serialize**
|
||||
Step 3: Serialize and de-serialize
|
||||
|
||||
The following JUnit snippet first creates an actor using the default constructor. The actor is, as we saw above a stateless one. Then it is serialized and de-serialized to get back the original actor. Being stateless, the de-serialized version behaves in the same way on a message as the original actor.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorTimeoutException;
|
||||
import akka.actor.Actors;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.serialization.Format;
|
||||
import akka.serialization.StatelessActorFormat;
|
||||
import static akka.serialization.ActorSerialization.*;
|
||||
|
||||
@Test public void mustBeAbleToSerializeAfterCreateActorRefFromClass() {
|
||||
ActorRef ref = Actors.actorOf(SerializationTestActor.class);
|
||||
assertNotNull(ref);
|
||||
|
|
@ -91,60 +85,69 @@ The following JUnit snippet first creates an actor using the default constructor
|
|||
}
|
||||
|
||||
Serialization of a Stateful Actor
|
||||
=================================
|
||||
---------------------------------
|
||||
|
||||
Let's now have a look at how to serialize an actor that carries a state with it. Here the expectation is that the serialization of the actor will also persist the state information. And after de-serialization we will get back the state with which it was serialized.
|
||||
|
||||
Step 1: Define the Actor
|
||||
------------------------
|
||||
|
||||
Here we consider an actor defined in Scala. We will however serialize using the Java APIs.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
class MyUntypedActor extends UntypedActor {
|
||||
var count = 0
|
||||
def onReceive(message: Any): Unit = message match {
|
||||
case m: String if m == "hello" =>
|
||||
count = count + 1
|
||||
getContext.replyUnsafe("world " + count)
|
||||
case m: String =>
|
||||
count = count + 1
|
||||
getContext.replyUnsafe("hello " + m + " " + count)
|
||||
case _ =>
|
||||
throw new Exception("invalid message type")
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
public class MyUntypedActor extends UntypedActor {
|
||||
int count = 0;
|
||||
|
||||
public void onReceive(Object msg) {
|
||||
if (msg.equals("hello")) {
|
||||
count = count + 1;
|
||||
getContext().replyUnsafe("world " + count);
|
||||
} else if (msg instanceof String) {
|
||||
count = count + 1;
|
||||
getContext().replyUnsafe("hello " + msg + " " + count);
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid message type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Note the actor has a state in the form of an Integer. And every message that the actor receives, it replies with an addition to the integer member.
|
||||
|
||||
Step 2: Define the instance of the typeclass
|
||||
--------------------------------------------
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.serialization.Format;
|
||||
import akka.serialization.SerializerFactory;
|
||||
|
||||
class MyUntypedActorFormat implements Format<MyUntypedActor> {
|
||||
@Override
|
||||
public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) {
|
||||
ProtobufProtocol.Counter p =
|
||||
(ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class);
|
||||
act.count_$eq(p.getCount());
|
||||
return act;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toBinary(MyUntypedActor ac) {
|
||||
return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray();
|
||||
}
|
||||
@Override
|
||||
public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) {
|
||||
ProtobufProtocol.Counter p =
|
||||
(ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class);
|
||||
act.count = p.getCount();
|
||||
return act;
|
||||
}
|
||||
|
||||
Note the usage of Protocol Buffers to serialize the state of the actor.
|
||||
@Override
|
||||
public byte[] toBinary(MyUntypedActor ac) {
|
||||
return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
Note the usage of Protocol Buffers to serialize the state of the actor. ProtobufProtocol.Counter is something
|
||||
you need to define yourself
|
||||
|
||||
Step 3: Serialize and de-serialize
|
||||
----------------------------------
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorTimeoutException;
|
||||
import akka.actor.Actors;
|
||||
import static akka.serialization.ActorSerialization.*;
|
||||
|
||||
@Test public void mustBeAbleToSerializeAStatefulActor() {
|
||||
ActorRef ref = Actors.actorOf(MyUntypedActor.class);
|
||||
assertNotNull(ref);
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
Software Transactional Memory (Java)
|
||||
====================================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Overview of STM
|
||||
===============
|
||||
---------------
|
||||
|
||||
An `STM <http://en.wikipedia.org/wiki/Software_transactional_memory>`_ turns the Java heap into a transactional data set with begin/commit/rollback semantics. Very much like a regular database. It implements the first three letters in ACID; ACI:
|
||||
* (failure) Atomicity: all changes during the execution of a transaction make it, or none make it. This only counts for transactional datastructures.
|
||||
|
|
@ -24,7 +28,7 @@ The STM is based on Transactional References (referred to as Refs). Refs are mem
|
|||
Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. The use of structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector.
|
||||
|
||||
Simple example
|
||||
==============
|
||||
--------------
|
||||
|
||||
Here is a simple example of an incremental counter using STM. This shows creating a ``Ref``, a transactional reference, and then modifying it within a transaction, which is delimited by an ``Atomic`` anonymous inner class.
|
||||
|
||||
|
|
@ -50,15 +54,14 @@ Here is a simple example of an incremental counter using STM. This shows creatin
|
|||
counter();
|
||||
// -> 2
|
||||
|
||||
----
|
||||
|
||||
Ref
|
||||
===
|
||||
---
|
||||
|
||||
Refs (transactional references) are mutable references to values and through the STM allow the safe sharing of mutable data. To ensure safety the value stored in a Ref should be immutable. The value referenced by a Ref can only be accessed or swapped within a transaction. Refs separate identity from value.
|
||||
|
||||
Creating a Ref
|
||||
--------------
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
You can create a Ref with or without an initial value.
|
||||
|
||||
|
|
@ -73,7 +76,7 @@ You can create a Ref with or without an initial value.
|
|||
final Ref<Integer> ref = new Ref<Integer>();
|
||||
|
||||
Accessing the value of a Ref
|
||||
----------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use ``get`` to access the value of a Ref. Note that if no initial value has been given then the value is initially ``null``.
|
||||
|
||||
|
|
@ -91,7 +94,7 @@ Use ``get`` to access the value of a Ref. Note that if no initial value has been
|
|||
// -> value = 0
|
||||
|
||||
Changing the value of a Ref
|
||||
---------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), which sets the new value and returns the old value.
|
||||
|
||||
|
|
@ -107,10 +110,9 @@ To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), whi
|
|||
}
|
||||
}.execute();
|
||||
|
||||
----
|
||||
|
||||
Transactions
|
||||
============
|
||||
------------
|
||||
|
||||
A transaction is delimited using an ``Atomic`` anonymous inner class.
|
||||
|
||||
|
|
@ -125,24 +127,24 @@ A transaction is delimited using an ``Atomic`` anonymous inner class.
|
|||
All changes made to transactional objects are isolated from other changes, all make it or non make it (so failure atomicity) and are consistent. With the AkkaSTM you automatically have the Oracle version of the SERIALIZED isolation level, lower isolation is not possible. To make it fully serialized, set the writeskew property that checks if a writeskew problem is allowed to happen.
|
||||
|
||||
Retries
|
||||
-------
|
||||
^^^^^^^
|
||||
|
||||
A transaction is automatically retried when it runs into some read or write conflict, until the operation completes, an exception (throwable) is thrown or when there are too many retries. When a read or writeconflict is encountered, the transaction uses a bounded exponential backoff to prevent cause more contention and give other transactions some room to complete.
|
||||
|
||||
If you are using non transactional resources in an atomic block, there could be problems because a transaction can be retried. If you are using print statements or logging, it could be that they are called more than once. So you need to be prepared to deal with this. One of the possible solutions is to work with a deferred or compensating task that is executed after the transaction aborts or commits.
|
||||
|
||||
Unexpected retries
|
||||
------------------
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It can happen for the first few executions that you get a few failures of execution that lead to unexpected retries, even though there is not any read or writeconflict. The cause of this is that speculative transaction configuration/selection is used. There are transactions optimized for a single transactional object, for 1..n and for n to unlimited. So based on the execution of the transaction, the system learns; it begins with a cheap one and upgrades to more expensive ones. Once it has learned, it will reuse this knowledge. It can be activated/deactivated using the speculative property on the TransactionFactoryBuilder. In most cases it is best use the default value (enabled) so you get more out of performance.
|
||||
|
||||
Coordinated transactions and Transactors
|
||||
----------------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you need coordinated transactions across actors or threads then see `Transactors <transactors-java>`_.
|
||||
If you need coordinated transactions across actors or threads then see :ref:`transactors-java`.
|
||||
|
||||
Configuring transactions
|
||||
------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It's possible to configure transactions. The ``Atomic`` class can take a ``TransactionFactory``, which can determine properties of the transaction. A default transaction factory is used if none is specified. You can create a ``TransactionFactory`` with a ``TransactionFactoryBuilder``.
|
||||
|
||||
|
|
@ -197,7 +199,7 @@ You can also specify the default values for some of these options in akka.conf.
|
|||
}
|
||||
|
||||
Transaction lifecycle listeners
|
||||
-------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It's possible to have code that will only run on the successful commit of a transaction, or when a transaction aborts. You can do this by adding ``deferred`` or ``compensating`` blocks to a transaction.
|
||||
|
||||
|
|
@ -225,7 +227,7 @@ It's possible to have code that will only run on the successful commit of a tran
|
|||
}.execute();
|
||||
|
||||
Blocking transactions
|
||||
---------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can block in a transaction until a condition is met by using an explicit ``retry``. To use ``retry`` you also need to configure the transaction to allow explicit retries.
|
||||
|
||||
|
|
@ -338,7 +340,7 @@ Here is an example of using ``retry`` to block until an account has enough money
|
|||
}
|
||||
|
||||
Alternative blocking transactions
|
||||
---------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can also have two alternative blocking transactions, one of which can succeed first, with ``EitherOrElse``.
|
||||
|
||||
|
|
@ -443,10 +445,9 @@ You can also have two alternative blocking transactions, one of which can succee
|
|||
}
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Transactional datastructures
|
||||
============================
|
||||
----------------------------
|
||||
|
||||
Akka provides two datastructures that are managed by the STM.
|
||||
|
||||
|
|
@ -510,26 +511,24 @@ Here is an example of creating and accessing a TransactionalVector:
|
|||
}
|
||||
}.execute();
|
||||
|
||||
----
|
||||
|
||||
Persistent datastructures
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
Akka's STM should only be used with immutable data. This can be costly if you have large datastructures and are using a naive copy-on-write. In order to make working with immutable datastructures fast enough Scala provides what are called Persistent Datastructures. There are currently two different ones:
|
||||
|
||||
- HashMap (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/HashMap.html>`_)
|
||||
- Vector (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html>`_)
|
||||
- HashMap (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/HashMap.html>`__)
|
||||
- Vector (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html>`__)
|
||||
|
||||
They are immutable and each update creates a completely new version but they are using clever structural sharing in order to make them almost as fast, for both read and update, as regular mutable datastructures.
|
||||
|
||||
This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009.
|
||||
|
||||
.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png
|
||||
.. image:: ../images/clojure-trees.png
|
||||
|
||||
----
|
||||
|
||||
JTA integration
|
||||
===============
|
||||
---------------
|
||||
|
||||
The STM has JTA (Java Transaction API) integration. This means that it will, if enabled, hook in to JTA and start a JTA transaction when the STM transaction is started. It will also rollback the STM transaction if the JTA transaction has failed and vice versa. This does not mean that the STM is made durable, if you need that you should use one of the `persistence modules <persistence>`_. It simply means that the STM will participate and interact with and external JTA provider, for example send a message using JMS atomically within an STM transaction, or use Hibernate to persist STM managed data etc.
|
||||
|
||||
|
|
@ -555,4 +554,4 @@ You also have to configure which JTA provider to use etc in the 'jta' config sec
|
|||
timeout = 60
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
**<span style="font-size: 150%;">Transactors (Java)</span>**
|
||||
============================================================
|
||||
.. _transactors-java:
|
||||
|
||||
Transactors (Java)
|
||||
==================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Why Transactors?
|
||||
================
|
||||
----------------
|
||||
|
||||
Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each operation needs to be atomic. For detailed discussion on the topic see `this JavaOne presentation <http://www.slideshare.net/jboner/state-youre-doing-it-wrong-javaone-2009>`_.
|
||||
|
||||
|
|
@ -15,21 +21,21 @@ Akka's Transactors combine Actors and STM to provide the best of the Actor model
|
|||
If you need Durability then you should not use one of the in-memory data structures but one of the persistent ones.
|
||||
|
||||
Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are:
|
||||
# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it.
|
||||
# When you want to share a datastructure across actors.
|
||||
# When you need to use the persistence modules.
|
||||
|
||||
- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it.
|
||||
- When you want to share a datastructure across actors.
|
||||
- When you need to use the persistence modules.
|
||||
|
||||
Actors and STM
|
||||
--------------
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
You can combine Actors and STM in several ways. An Actor may use STM internally so that particular changes are guaranteed to be atomic. Actors may also share transactional datastructures as the STM provides safe shared state across threads.
|
||||
|
||||
It's also possible to coordinate transactions across Actors or threads so that either the transactions in a set all commit successfully or they all fail. This is the focus of Transactors and the explicit support for coordinated transactions in this section.
|
||||
|
||||
----
|
||||
|
||||
Coordinated transactions
|
||||
========================
|
||||
------------------------
|
||||
|
||||
Akka provides an explicit mechanism for coordinating transactions across actors. Under the hood it uses a ``CountDownCommitBarrier``, similar to a CountDownLatch.
|
||||
|
||||
|
|
@ -40,9 +46,11 @@ Here is an example of coordinating two simple counter UntypedActors so that they
|
|||
import akka.actor.ActorRef;
|
||||
|
||||
public class Increment {
|
||||
private ActorRef friend = null;
|
||||
private final ActorRef friend;
|
||||
|
||||
public Increment() {}
|
||||
public Increment() {
|
||||
this.friend = null;
|
||||
}
|
||||
|
||||
public Increment(ActorRef friend) {
|
||||
this.friend = friend;
|
||||
|
|
@ -59,9 +67,7 @@ Here is an example of coordinating two simple counter UntypedActors so that they
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.UntypedActor;
|
||||
import static akka.actor.Actors.*;
|
||||
import akka.stm.Ref;
|
||||
import akka.transactor.Atomically;
|
||||
import akka.transactor.Coordinated;
|
||||
|
|
@ -88,11 +94,8 @@ Here is an example of coordinating two simple counter UntypedActors so that they
|
|||
}
|
||||
});
|
||||
}
|
||||
} else if (incoming instanceof String) {
|
||||
String message = (String) incoming;
|
||||
if (message.equals("GetCount")) {
|
||||
getContext().replyUnsafe(count.get());
|
||||
}
|
||||
} else if (incoming.equals("GetCount")) {
|
||||
getContext().replyUnsafe(count.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +107,7 @@ Here is an example of coordinating two simple counter UntypedActors so that they
|
|||
|
||||
counter1.sendOneWay(new Coordinated(new Increment(counter2)));
|
||||
|
||||
To start a new coordinated transaction set that you will also participate in, just create a ``Coordinated`` object:
|
||||
To start a new coordinated transaction that you will also participate in, just create a ``Coordinated`` object:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
|
|
@ -116,7 +119,7 @@ To start a coordinated transaction that you won't participate in yourself you ca
|
|||
|
||||
actor.sendOneWay(new Coordinated(new Message()));
|
||||
|
||||
To include another actor in the same coordinated transaction set that you've created or received, use the ``coordinate`` method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent.
|
||||
To include another actor in the same coordinated transaction that you've created or received, use the ``coordinate`` method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
|
|
@ -134,10 +137,9 @@ To enter the coordinated transaction use the atomic method of the coordinated ob
|
|||
|
||||
The coordinated transaction will wait for the other transactions before committing. If any of the coordinated transactions fail then they all fail.
|
||||
|
||||
----
|
||||
|
||||
UntypedTransactor
|
||||
=================
|
||||
-----------------
|
||||
|
||||
UntypedTransactors are untyped actors that provide a general pattern for coordinating transactions, using the explicit coordination described above.
|
||||
|
||||
|
|
@ -146,10 +148,12 @@ Here's an example of a simple untyped transactor that will join a coordinated tr
|
|||
.. code-block:: java
|
||||
|
||||
import akka.transactor.UntypedTransactor;
|
||||
import akka.stm.Ref;
|
||||
|
||||
public class Counter extends UntypedTransactor {
|
||||
Ref<Integer> count = new Ref<Integer>(0);
|
||||
|
||||
@Override
|
||||
public void atomically(Object message) {
|
||||
if (message instanceof Increment) {
|
||||
count.set(count.get() + 1);
|
||||
|
|
@ -174,7 +178,8 @@ Example of coordinating an increment, similar to the explicitly coordinated exam
|
|||
public class Counter extends UntypedTransactor {
|
||||
Ref<Integer> count = new Ref<Integer>(0);
|
||||
|
||||
@Override public Set<SendTo> coordinate(Object message) {
|
||||
@Override
|
||||
public Set<SendTo> coordinate(Object message) {
|
||||
if (message instanceof Increment) {
|
||||
Increment increment = (Increment) message;
|
||||
if (increment.hasFriend())
|
||||
|
|
@ -183,6 +188,7 @@ Example of coordinating an increment, similar to the explicitly coordinated exam
|
|||
return nobody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void atomically(Object message) {
|
||||
if (message instanceof Increment) {
|
||||
count.set(count.get() + 1);
|
||||
|
|
@ -190,14 +196,13 @@ Example of coordinating an increment, similar to the explicitly coordinated exam
|
|||
}
|
||||
}
|
||||
|
||||
To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. These methods also expect partial functions like the receive method. They do not execute within the transaction.
|
||||
To execute directly before or after the coordinated transaction, override the ``before`` and ``after`` methods. They do not execute within the transaction.
|
||||
|
||||
To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions.
|
||||
|
||||
----
|
||||
|
||||
Coordinating Typed Actors
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
It's also possible to use coordinated transactions with typed actors. You can explicitly pass around ``Coordinated`` objects, or use built-in support with the ``@Coordinated`` annotation and the ``Coordination.coordinate`` method.
|
||||
|
||||
|
|
@ -249,17 +254,18 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr
|
|||
}
|
||||
}
|
||||
|
||||
`<code format="java">`_
|
||||
Counter counter1 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class);
|
||||
Counter counter2 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class);
|
||||
.. code-block:: java
|
||||
|
||||
Coordination.coordinate(true, new Atomically() {
|
||||
Counter counter1 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class);
|
||||
Counter counter2 = (Counter) TypedActor.newInstance(Counter.class, CounterImpl.class);
|
||||
|
||||
Coordination.coordinate(true, new Atomically() {
|
||||
public void atomically() {
|
||||
counter1.increment();
|
||||
counter2.increment();
|
||||
counter1.increment();
|
||||
counter2.increment();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
TypedActor.stop(counter1);
|
||||
TypedActor.stop(counter2);
|
||||
|
||||
TypedActor.stop(counter1);
|
||||
TypedActor.stop(counter2);
|
||||
`<code>`_
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
Typed Actors (Java)
|
||||
===================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
The Typed Actors are implemented through `Typed Actors <http://en.wikipedia.org/wiki/Active_object>`_. It uses AOP through `AspectWerkz <http://aspectwerkz.codehaus.org/>`_ to turn regular POJOs into asynchronous non-blocking Actors with semantics of the Actor Model. E.g. each message dispatch is turned into a message that is put on a queue to be processed by the Typed Actor sequentially one by one.
|
||||
|
|
@ -171,7 +175,7 @@ Here is an example how you can use it to in a 'void' (e.g. fire-forget) method t
|
|||
}
|
||||
}
|
||||
|
||||
If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with scenario.
|
||||
If the sender, sender future etc. is not available, then these methods will return 'null' so you should have a way of dealing with that scenario.
|
||||
|
||||
Messages and immutability
|
||||
-------------------------
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
Actors (Java)
|
||||
=============
|
||||
|
||||
=
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
|
|
@ -16,11 +18,15 @@ Here is an example:
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.event.EventHandler;
|
||||
|
||||
public class SampleUntypedActor extends UntypedActor {
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof String)
|
||||
EventHandler.info(this, String.format("Received String message: %s", message));
|
||||
EventHandler.info(this, String.format("Received String message: %s",
|
||||
message));
|
||||
else
|
||||
throw new IllegalArgumentException("Unknown message: " + message);
|
||||
}
|
||||
|
|
@ -409,8 +415,9 @@ Actor life-cycle
|
|||
|
||||
The actor has a well-defined non-circular life-cycle.
|
||||
|
||||
`<code>`_
|
||||
NEW (newly created actor) - can't receive messages (yet)
|
||||
=> STARTED (when 'start' is invoked) - can receive messages
|
||||
=> SHUT DOWN (when 'exit' or 'stop' is invoked) - can't do anything
|
||||
`<code>`_
|
||||
::
|
||||
|
||||
NEW (newly created actor) - can't receive messages (yet)
|
||||
=> STARTED (when 'start' is invoked) - can receive messages
|
||||
=> SHUT DOWN (when 'exit' or 'stop' is invoked) - can't do anything
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
Akka
|
||||
====
|
||||
|
||||
**Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors**
|
||||
|
||||
We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction. Akka is here to change that. Using the Actor Model together with Software Transactional Memory we raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. For fault-tolerance we adopt the "Let it crash" / "Embrace failure" model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the Apache 2 License.
|
||||
|
||||
Akka is split up into two different parts:
|
||||
* Akka - Reflects all the sections under 'Scala API' and 'Java API' in the navigation bar.
|
||||
* Akka Modules - Reflects all the sections under 'Add-on modules' in the navigation bar.
|
||||
|
||||
Download from `<http://akka.io/downloads/>`_
|
||||
|
||||
News: Akka 1.0 final is released
|
||||
--------------------------------
|
||||
|
||||
1.0 documentation
|
||||
-----------------
|
||||
|
||||
This documentation covers the latest release ready code in 'master' branch in the repository.
|
||||
If you want the documentation for the 1.0 release you can find it `here <http://akka.io/docs/akka-1.0/space.menu.html>`_.
|
||||
|
||||
You can watch the recording of the `Akka talk at JFokus in Feb 2011 <http://79.136.112.58/ability/show/xaimkwdli/a2_20110216_1110/mainshow.asp?STREAMID=1>`_.
|
||||
|
||||
`<media type="custom" key="8924178">`_
|
||||
|
||||
**Akka implements a unique hybrid of:**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* `Actors <untyped-actors-java>`_, which gives you:
|
||||
* Simple and high-level abstractions for concurrency and parallelism.
|
||||
* Asynchronous, non-blocking and highly performant event-driven programming model.
|
||||
* Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM).
|
||||
* `Failure management <fault-tolerance-java>`_ through supervisor hierarchies with `let-it-crash <http://letitcrash.com>`_ semantics. Excellent for writing highly fault-tolerant systems that never stop, systems that self-heal.
|
||||
* `Software Transactional Memory <stm-java>`_ (STM). (Distributed transactions coming soon).
|
||||
* `Transactors <transactors-java>`_: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic retry and rollback.
|
||||
* `Remote actors <remote-actors-java>`_: highly performant distributed actors with remote supervision and error management.
|
||||
* Java and Scala API.
|
||||
|
||||
**Akka also has a set of add-on modules:**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* `Camel <camel>`_: Expose actors as Apache Camel endpoints.
|
||||
* `Spring <spring-integration>`_: Wire up typed actors in the Spring config using Akka's namespace.
|
||||
* `REST <rest>`_ (JAX-RS): Expose actors as REST services.
|
||||
* `OSGi <osgi>`_: Akka and all its dependency is OSGi enabled.
|
||||
* `Mist <http#Mist%20-%20Lightweight%20Asynchronous%20HTTP>`_: Expose actors as asynchronous HTTP services.
|
||||
* `Security <security>`_: Basic, Digest and Kerberos based security.
|
||||
* `Microkernel <microkernel>`_: Run Akka as a stand-alone self-hosted kernel.
|
||||
* `FSM <fsm-scala>`_: Finite State Machine support.
|
||||
* `JTA <jta>`_: Let the STM interoperate with other transactional resources.
|
||||
* `Pub/Sub <pubsub>`_: Publish-Subscribe across remote nodes.
|
||||
|
||||
**Akka can be used in two different ways:**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* As a library: used by a web app, to be put into ‘WEB-INF/lib’ or as a regular JAR on your classpath.
|
||||
* As a microkernel: stand-alone kernel, embedding a servlet container and all the other modules.
|
||||
|
||||
See the `Use-case and Deployment Scenarios <deployment-scenarios>`_ for details.
|
||||
|
|
@ -316,7 +316,7 @@ Supervised actors have the option to reply to the initial sender within preResta
|
|||
self.reply_?(reason.getMessage)
|
||||
}
|
||||
|
||||
override def postStop {
|
||||
override def postStop() {
|
||||
self.reply_?("stopped by supervisor")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ Finally, bind the *handleHttpRequest* function of the *Endpoint* trait to the ac
|
|||
//
|
||||
// this is where you want attach your endpoint hooks
|
||||
//
|
||||
override def preStart = {
|
||||
override def preStart() = {
|
||||
//
|
||||
// we expect there to be one root and that it's already been started up
|
||||
// obviously there are plenty of other ways to obtaining this actor
|
||||
|
|
@ -397,7 +397,7 @@ As noted above, hook functions are non-exclusive. This means multiple actors can
|
|||
//
|
||||
// this is where you want attach your endpoint hooks
|
||||
//
|
||||
override def preStart = {
|
||||
override def preStart() = {
|
||||
//
|
||||
// we expect there to be one root and that it's already been started up
|
||||
// obviously there are plenty of other ways to obtaining this actor
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
Issue Tracking
|
||||
==============
|
||||
|
||||
Akka is using Assembla as issue tracking system.
|
||||
|
||||
Browsing
|
||||
--------
|
||||
|
||||
You can find the Akka tickets here: `<http://www.assembla.com/spaces/akka>`_
|
||||
You can find the Akka Modules tickets here: `<https://www.assembla.com/spaces/akka-modules/tickets>`_
|
||||
|
||||
The roadmap for each milestone is here: `<https://www.assembla.com/spaces/akka/milestones>`_
|
||||
|
||||
Creating tickets
|
||||
----------------
|
||||
|
||||
In order to create tickets you need to do the following:
|
||||
|
||||
# Register here: `<https://www.assembla.com/user/signup>`_
|
||||
# Log in
|
||||
|
||||
For Akka tickets:
|
||||
|
||||
# Create the ticket: `<https://www.assembla.com/spaces/akka/tickets/new>`_
|
||||
|
||||
|
||||
For Akka Modules tickets:
|
||||
|
||||
# Create the ticket: `<https://www.assembla.com/spaces/akka-modules/tickets>`_
|
||||
|
||||
Thanks a lot for reporting bugs and suggesting features.
|
||||
|
||||
Failing test
|
||||
------------
|
||||
|
||||
Please submit a failing test on the following format:
|
||||
|
||||
`<code format="scala">`_
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class Ticket001Spec extends WordSpec with MustMatchers {
|
||||
|
||||
"An XXX" should {
|
||||
"do YYY" in {
|
||||
1 must be (1)
|
||||
}
|
||||
}
|
||||
}
|
||||
`<code>`_
|
||||
|
|
@ -1,432 +0,0 @@
|
|||
Migration guide from 0.10.x to 1.0.x
|
||||
====================================
|
||||
|
||||
----
|
||||
|
||||
Akka & Akka Modules separated into two different repositories and distributions
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Akka is split up into two different parts:
|
||||
* Akka - Reflects all the sections under 'Scala API' and 'Java API' in the navigation bar.
|
||||
* Akka Modules - Reflects all the sections under 'Add-on modules' in the navigation bar.
|
||||
|
||||
Download the release you need (Akka core or Akka Modules) from `<http://akka.io/downloads>`_ and unzip it.
|
||||
|
||||
----
|
||||
|
||||
Changed Akka URI
|
||||
----------------
|
||||
|
||||
http:*akkasource.org changed to http:*akka.io
|
||||
|
||||
Reflects XSDs, Maven repositories, ScalaDoc etc.
|
||||
|
||||
----
|
||||
|
||||
Removed 'se.scalablesolutions' prefix
|
||||
-------------------------------------
|
||||
|
||||
We have removed some boilerplate by shortening the Akka package from
|
||||
**se.scalablesolutions.akka** to just **akka** so just do a search-replace in your project,
|
||||
we apologize for the inconvenience, but we did it for our users.
|
||||
|
||||
----
|
||||
|
||||
Akka-core is no more
|
||||
--------------------
|
||||
|
||||
Akka-core has been split into akka-actor, akka-stm, akka-typed-actor & akka-remote this means that you need to update any deps you have on akka-core.
|
||||
|
||||
----
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
Turning on/off modules
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All the 'service = on' elements for turning modules on and off have been replaced by a top-level list of the enabled services.
|
||||
|
||||
Services available for turning on/off are:
|
||||
* "remote"
|
||||
* "http"
|
||||
* "camel"
|
||||
|
||||
**All** services are **OFF** by default. Enable the ones you are using.
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
akka {
|
||||
enabled-modules = [] # Comma separated list of the enabled modules. Options: ["remote", "camel", "http"]
|
||||
}
|
||||
|
||||
Renames
|
||||
^^^^^^^
|
||||
|
||||
* 'rest' section - has been renamed to 'http' to align with the module name 'akka-http'.
|
||||
* 'storage' section - has been renamed to 'persistence' to align with the module name 'akka-persistence'.
|
||||
|
||||
.. code-block:: ruby
|
||||
|
||||
akka {
|
||||
http {
|
||||
..
|
||||
}
|
||||
|
||||
persistence {
|
||||
..
|
||||
}
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Important changes from RC2-RC3
|
||||
------------------------------
|
||||
|
||||
**akka.config.SupervisionSupervise**
|
||||
def apply(actorRef: ActorRef, lifeCycle: LifeCycle, registerAsRemoteService: Boolean = false)
|
||||
- boolean instead of remoteAddress, registers that actor with it's id as service name on the local server
|
||||
|
||||
**akka.actor.Actors now is the API for Java to interact with Actors, Remoting and ActorRegistry:**
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
*actorOf()..*
|
||||
remote().actorOf()...
|
||||
*registry().actorsFor("foo")...*
|
||||
|
||||
***akka.actor.Actor now is the API for Scala to interact with Actors, Remoting and ActorRegistry:***
|
||||
|
||||
*import akka.actor.Actor._*
|
||||
actorOf()...
|
||||
*remote.actorOf()...*
|
||||
registry.actorsFor("foo")
|
||||
|
||||
**object UntypedActor has been deleted and replaced with akka.actor.Actors/akka.actor.Actor (Java/Scala)**
|
||||
UntypedActor.actorOf -> Actors.actorOf (Java) or Actor.actorOf (Scala)
|
||||
|
||||
**object ActorRegistry has been deleted and replaced with akka.actor.Actors.registry()/akka.actor.Actor.registry (Java/Scala)**
|
||||
ActorRegistry. -> Actors.registry(). (Java) or Actor.registry. (Scala)
|
||||
|
||||
**object RemoteClient has been deleted and replaced with akka.actor.Actors.remote()/akka.actor.Actor.remote (Java/Scala)**
|
||||
RemoteClient -> Actors.remote() (Java) or Actor.remote (Scala)
|
||||
|
||||
**object RemoteServer has been deleted and replaced with akka.actor.Actors.remote()/akka.actor.Actor.remote (Java/Scala)**
|
||||
RemoteServer - deleted -> Actors.remote() (Java) or Actor.remote (Scala)
|
||||
|
||||
**classes RemoteActor, RemoteUntypedActor and RemoteUntypedConsumerActors has been deleted and replaced**
|
||||
**with akka.actor.Actors.remote().actorOf(x, host port)/akka.actor.Actor.remote.actorOf(x, host, port)**
|
||||
RemoteActor, RemoteUntypedActor - deleted, use: remote().actorOf(YourActor.class, host, port) (Java) or remote.actorOf[YourActor](host, port)
|
||||
|
||||
**Remoted spring-actors now default to spring id as service-name, use "service-name" attribute on "remote"-tag to override**
|
||||
|
||||
**Listeners for RemoteServer and RemoteClient** are now registered on Actors.remote().addListener (Java) or Actor.remote.addListener (Scala),
|
||||
this means that all listeners get all remote events, both remote server evens and remote client events, **so adjust your code accordingly.**
|
||||
|
||||
**ActorRef.startLinkRemote has been removed since one specified on creation wether the actor is client-managed or not.**
|
||||
|
||||
Important change from RC3 to RC4
|
||||
--------------------------------
|
||||
|
||||
The Akka-Spring namespace has changed from akkasource.org and scalablesolutions.se to http:*akka.io/schema and http:*akka.io/akka-<version>.xsd
|
||||
|
||||
----
|
||||
|
||||
Module akka-actor
|
||||
-----------------
|
||||
|
||||
The Actor.init callback has been renamed to "preStart" to align with the general callback naming and is more clear about when it's called.
|
||||
|
||||
The Actor.shutdown callback has been renamed to "postStop" to align with the general callback naming and is more clear about when it's called.
|
||||
|
||||
The Actor.initTransactionalState callback has been removed, logic should be moved to preStart and be wrapped in an atomic block
|
||||
|
||||
**se.scalablesolutions.akka.config.ScalaConfig** and **se.scalablesolutions.akka.config.JavaConfig** have been merged into **akka.config.Supervision**
|
||||
|
||||
**RemoteAddress** has moved from **se.scalablesolutions.akka.config.ScalaConfig** to **akka.config**
|
||||
|
||||
The ActorRef.lifeCycle has changed signature from Option[LifeCycle] to LifeCycle, this means you need to change code that looks like this:
|
||||
**self.lifeCycle = Some(LifeCycle(Permanent))** to **self.lifeCycle = Permanent**
|
||||
|
||||
The equivalent to **self.lifeCycle = None** is **self.lifeCycle = UndefinedLifeCycle**
|
||||
**LifeCycle(Permanent)** becomes **Permanent**
|
||||
**new LifeCycle(permanent())** becomes **permanent()** (need to do: import static se.scalablesolutions.akka.config.Supervision.*; first)
|
||||
|
||||
**JavaConfig.Component** and **ScalaConfig.Component** have been consolidated and renamed as **Supervision.SuperviseTypedActor**
|
||||
|
||||
**self.trapExit** has been moved into the FaultHandlingStrategy, and **ActorRef.faultHandler** has switched type from Option[FaultHandlingStrategy]
|
||||
to FaultHandlingStrategy:
|
||||
|
||||
|| **Scala** ||
|
||||
||
|
||||
`<code format="scala">`_
|
||||
import akka.config.Supervision._
|
||||
|
||||
self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 3, 5000)
|
||||
|
||||
`<code>`_ ||
|
||||
|| **Java** ||
|
||||
||
|
||||
`<code format="java">`_
|
||||
import static akka.Supervision.*;
|
||||
|
||||
getContext().setFaultHandler(new OneForOneStrategy(new Class[] { Exception.class },50,1000))
|
||||
|
||||
`<code>`_ ||
|
||||
|
||||
**RestartStrategy, AllForOne, OneForOne** have been replaced with **AllForOneStrategy** and **OneForOneStrategy** in **se.scalablesolutions.akka.config.Supervision**
|
||||
|
||||
|| **Scala** ||
|
||||
||
|
||||
`<code format="scala">`_
|
||||
import akka.config.Supervision._
|
||||
SupervisorConfig(
|
||||
OneForOneStrategy(List(classOf[Exception]), 3, 5000),
|
||||
Supervise(pingpong1,Permanent) :: Nil
|
||||
)
|
||||
|
||||
`<code>`_ ||
|
||||
|| **Java** ||
|
||||
||
|
||||
`<code format="java">`_
|
||||
import static akka.Supervision.*;
|
||||
|
||||
new SupervisorConfig(
|
||||
new OneForOneStrategy(new Class[] { Exception.class },50,1000),
|
||||
new Server[] { new Supervise(pingpong1, permanent()) }
|
||||
)
|
||||
|
||||
`<code>`_ ||
|
||||
|
||||
We have removed the following factory methods:
|
||||
|
||||
**Actor.actor { case foo => bar }**
|
||||
**Actor.transactor { case foo => bar }**
|
||||
**Actor.temporaryActor { case foo => bar }**
|
||||
**Actor.init {} receive { case foo => bar }**
|
||||
|
||||
They started the actor and no config was possible, it was inconsistent and irreparable.
|
||||
|
||||
replace with your own factories, or:
|
||||
|
||||
**actorOf( new Actor { def receive = { case foo => bar } } ).start**
|
||||
**actorOf( new Actor { self.lifeCycle = Temporary; def receive = { case foo => bar } } ).start**
|
||||
|
||||
ReceiveTimeout is now rescheduled after every message, before there was only an initial timeout.
|
||||
To stop rescheduling of ReceiveTimeout, set **receiveTimeout = None**
|
||||
|
||||
HotSwap
|
||||
-------
|
||||
|
||||
HotSwap does no longer use behavior stacking by default, but that is an option to both "become" and HotSwap.
|
||||
|
||||
HotSwap now takes for Scala a Function from ActorRef to a Receive, the ActorRef passed in is the reference to self, so you can do self.reply() etc.
|
||||
|
||||
----
|
||||
|
||||
Module akka-stm
|
||||
---------------
|
||||
|
||||
The STM stuff is now in its own module. This means that there is no support for transactions or transactors in akka-actor.
|
||||
|
||||
Local and global
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The **local/global** distinction has been dropped. This means that if the following general import was being used:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.stm.local._
|
||||
|
||||
this is now just:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.stm._
|
||||
|
||||
Coordinated is the new global
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There is a new explicit mechanism for coordinated transactions. See the `Scala Transactors <transactors-scala>`_ and `Java Transactors <transactors-java>`_ documentation for more information. Coordinated transactions and transactors are found in the ``akka.transactor`` package now. The usage of transactors has changed.
|
||||
|
||||
Agents
|
||||
^^^^^^
|
||||
|
||||
Agent is now in the akka-stm module and has moved to the ``akka.agent`` package. The implementation has been reworked and is now closer to Clojure agents. There is not much difference in general usage, the main changes involve interaction with the STM.
|
||||
|
||||
While updates to Agents are asynchronous, the state of an Agent is always immediately available for reading by any thread. Agents are integrated with the STM - any dispatches made in a transaction are held until that transaction commits, and are discarded if it is retried or aborted. There is a new ``sendOff`` method for long-running or blocking update functions.
|
||||
|
||||
----
|
||||
|
||||
Module akka-camel
|
||||
-----------------
|
||||
|
||||
Access to the CamelService managed by CamelServiceManager has changed:
|
||||
|
||||
* Method service renamed to mandatoryService (Scala)
|
||||
* Method service now returns Option[CamelService] (Scala)
|
||||
* Introduced method getMandatoryService() (Java)
|
||||
* Introduced method getService() (Java)
|
||||
|
||||
|| **Scala** ||
|
||||
||
|
||||
`<code format="scala">`_
|
||||
import se.scalablesolutions.akka.camel.CamelServiceManager._
|
||||
import se.scalablesolutions.akka.camel.CamelService
|
||||
|
||||
val o: Option[CamelService] = service
|
||||
val s: CamelService = mandatoryService
|
||||
|
||||
`<code>`_ ||
|
||||
|| **Java** ||
|
||||
||
|
||||
`<code format="java">`_
|
||||
import se.scalablesolutions.akka.camel.CamelService;
|
||||
import se.scalablesolutions.akka.japi.Option;
|
||||
import static se.scalablesolutions.akka.camel.CamelServiceManager.*;
|
||||
|
||||
Option<CamelService> o = getService();
|
||||
CamelService s = getMandatoryService();
|
||||
|
||||
`<code>`_ ||
|
||||
|
||||
Access to the CamelContext and ProducerTemplate managed by CamelContextManager has changed:
|
||||
|
||||
* Method context renamed to mandatoryContext (Scala)
|
||||
* Method template renamed to mandatoryTemplate (Scala)
|
||||
* Method service now returns Option[CamelContext] (Scala)
|
||||
* Method template now returns Option[ProducerTemplate] (Scala)
|
||||
* Introduced method getMandatoryContext() (Java)
|
||||
* Introduced method getContext() (Java)
|
||||
* Introduced method getMandatoryTemplate() (Java)
|
||||
* Introduced method getTemplate() (Java)
|
||||
|
||||
|| **Scala** ||
|
||||
||
|
||||
`<code format="scala">`_
|
||||
import org.apache.camel.CamelContext
|
||||
import org.apache.camel.ProducerTemplate
|
||||
|
||||
import se.scalablesolutions.akka.camel.CamelContextManager._
|
||||
|
||||
val co: Option[CamelContext] = context
|
||||
val to: Option[ProducerTemplate] = template
|
||||
|
||||
val c: CamelContext = mandatoryContext
|
||||
val t: ProducerTemplate = mandatoryTemplate
|
||||
|
||||
`<code>`_ ||
|
||||
|| **Java** ||
|
||||
||
|
||||
`<code format="java">`_
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.ProducerTemplate;
|
||||
|
||||
import se.scalablesolutions.akka.japi.Option;
|
||||
import static se.scalablesolutions.akka.camel.CamelContextManager.*;
|
||||
|
||||
Option<CamelContext> co = getContext();
|
||||
Option<ProducerTemplate> to = getTemplate();
|
||||
|
||||
CamelContext c = getMandatoryContext();
|
||||
ProducerTemplate t = getMandatoryTemplate();
|
||||
|
||||
`<code>`_ ||
|
||||
|
||||
The following methods have been renamed on class se.scalablesolutions.akka.camel.Message:
|
||||
|
||||
* bodyAs(Class) has been renamed to getBodyAs(Class)
|
||||
* headerAs(String, Class) has been renamed to getHeaderAs(String, Class)
|
||||
|
||||
The API for waiting for consumer endpoint activation and de-activation has been changed
|
||||
|
||||
* CamelService.expectEndpointActivationCount has been removed and replaced by CamelService.awaitEndpointActivation
|
||||
* CamelService.expectEndpointDeactivationCount has been removed and replaced by CamelService.awaitEndpointDeactivation
|
||||
|
||||
|| **Scala** ||
|
||||
||
|
||||
`<code format="scala">`_
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import se.scalablesolutions.akka.camel.CamelServiceManager._
|
||||
|
||||
val s = startCamelService
|
||||
val actor = Actor.actorOf[SampleConsumer]
|
||||
|
||||
// wait for 1 consumer being activated
|
||||
s.awaitEndpointActivation(1) {
|
||||
actor.start
|
||||
}
|
||||
|
||||
// wait for 1 consumer being de-activated
|
||||
s.awaitEndpointDeactivation(1) {
|
||||
actor.stop
|
||||
}
|
||||
|
||||
s.stop
|
||||
|
||||
`<code>`_ ||
|
||||
|| **Java** ||
|
||||
||
|
||||
`<code format="java">`_
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import se.scalablesolutions.akka.actor.ActorRef;
|
||||
import se.scalablesolutions.akka.actor.Actors;
|
||||
import se.scalablesolutions.akka.camel.CamelService;
|
||||
import se.scalablesolutions.akka.japi.SideEffect;
|
||||
import static se.scalablesolutions.akka.camel.CamelServiceManager.*;
|
||||
|
||||
CamelService s = startCamelService();
|
||||
final ActorRef actor = Actors.actorOf(SampleUntypedConsumer.class);
|
||||
|
||||
// wait for 1 consumer being activated
|
||||
s.awaitEndpointActivation(1, new SideEffect() {
|
||||
public void apply() {
|
||||
actor.start();
|
||||
}
|
||||
});
|
||||
|
||||
// wait for 1 consumer being de-activated
|
||||
s.awaitEndpointDeactivation(1, new SideEffect() {
|
||||
public void apply() {
|
||||
actor.stop();
|
||||
}
|
||||
});
|
||||
|
||||
s.stop();
|
||||
|
||||
`<code>`_ ||
|
||||
|
||||
-
|
||||
|
||||
Module Akka-Http
|
||||
----------------
|
||||
|
||||
Atmosphere support has been removed. If you were using akka.comet.AkkaServlet for Jersey support only,
|
||||
you can switch that to: akka.http.AkkaRestServlet and it should work just like before.
|
||||
|
||||
Atmosphere has been removed because we have a new async http support in the form of Akka Mist, a very thin bridge
|
||||
between Servlet3.0/JettyContinuations and Actors, enabling Http-as-messages, read more about it here:
|
||||
http://doc.akka.io/http#Mist%20-%20Lightweight%20Asynchronous%20HTTP
|
||||
|
||||
If you really need Atmosphere support, you can add it yourself by following the steps listed at the start of:
|
||||
http://doc.akka.io/comet
|
||||
|
||||
Module akka-spring
|
||||
------------------
|
||||
|
||||
The Akka XML schema URI has changed to http://akka.io/schema/akka
|
||||
|
||||
`<code format="xml">`_
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:akka="http://akka.io/schema/akka"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://akka.io/schema/akka
|
||||
http://akka.io/akka-1.0.xsd">
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
</beans>
|
||||
|
||||
`<code>`_
|
||||
|
|
@ -219,18 +219,16 @@ Examples
|
|||
with SmallestMailboxSelector
|
||||
with BasicNoBackoffFilter
|
||||
{
|
||||
def factory = actorOf(new Actor {def receive = {case n:Int =>
|
||||
Thread.sleep(n)
|
||||
counter.incrementAndGet
|
||||
latch.countDown()}})
|
||||
|
||||
def receive = _route
|
||||
def lowerBound = 2
|
||||
def upperBound = 4
|
||||
def rampupRate = 0.1
|
||||
def partialFill = true
|
||||
def selectionCount = 1
|
||||
def instance = factory
|
||||
def receive = _route
|
||||
def instance = actorOf(new Actor {def receive = {case n:Int =>
|
||||
Thread.sleep(n)
|
||||
counter.incrementAndGet
|
||||
latch.countDown()}})
|
||||
}
|
||||
|
||||
.. code-block:: scala
|
||||
|
|
@ -243,11 +241,7 @@ Examples
|
|||
with RunningMeanBackoff
|
||||
with BasicRampup
|
||||
{
|
||||
|
||||
def factory = actorOf(new Actor {def receive = {case n:Int =>
|
||||
Thread.sleep(n)
|
||||
latch.countDown()}})
|
||||
|
||||
def receive = _route
|
||||
def lowerBound = 1
|
||||
def upperBound = 5
|
||||
def pressureThreshold = 1
|
||||
|
|
@ -256,8 +250,9 @@ Examples
|
|||
def rampupRate = 0.1
|
||||
def backoffRate = 0.50
|
||||
def backoffThreshold = 0.50
|
||||
def instance = factory
|
||||
def receive = _route
|
||||
def instance = actorOf(new Actor {def receive = {case n:Int =>
|
||||
Thread.sleep(n)
|
||||
latch.countDown()}})
|
||||
}
|
||||
|
||||
Taken from the unit test `spec <https://github.com/jboner/akka/blob/master/akka-actor/src/test/scala/akka/routing/RoutingSpec.scala>`_.
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
Scheduler
|
||||
=========
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Akka has a little scheduler written using actors. Can be convenient if you want to schedule some periodic task for maintenance or similar.
|
||||
|
||||
It allows you to register a message that you want to be sent to a specific actor at a periodic interval. Here is an example:
|
||||
|
||||
`<code format="scala">`_
|
||||
//Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages
|
||||
Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit)
|
||||
|
||||
//Sends messageToBeSent to receiverActor after delayUntilSend
|
||||
Scheduler.scheduleOnce(receiverActor, messageToBeSent, delayUntilSend, timeUnit)
|
||||
`<code>`_
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
Team
|
||||
=====
|
||||
|
||||
|| **Name** || **Role** || **Email** ||
|
||||
|| Jonas Bonér || Founder, Despot, Committer || jonas AT jonasboner DOT com ||
|
||||
|| Viktor Klang || Bad cop, Committer || viktor DOT klang AT gmail DOT com ||
|
||||
|| Debasish Ghosh || Committer || dghosh AT acm DOT org ||
|
||||
|| Ross McDonald || Alumni || rossajmcd AT gmail DOT com ||
|
||||
|| Eckhart Hertzler || Alumni || ||
|
||||
|| Mikael Högqvist || Alumni || ||
|
||||
|| Tim Perrett || Alumni || ||
|
||||
|| Jeanfrancois Arcand || Alumni || jfarcand AT apache DOT org ||
|
||||
|| Martin Krasser || Committer || krasserm AT googlemail DOT com ||
|
||||
|| Jan Van Besien || Alumni || ||
|
||||
|| Michael Kober || Committer || ||
|
||||
|| Peter Vlugter || Committer || ||
|
||||
|| Peter Veentjer || Committer || ||
|
||||
|| Irmo Manie || Committer || ||
|
||||
|| Heiko Seeberger || Committer || ||
|
||||
|| Hiram Chirino || Committer || ||
|
||||
|| Scott Clasen || Committer || ||
|
||||
|| Roland Kuhn || Committer || ||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
Web Framework Integrations
|
||||
==========================
|
||||
|
||||
Play Framework
|
||||
==============
|
||||
|
||||
Home page: `<http://www.playframework.org/>`_
|
||||
Akka Play plugin: `<https://github.com/dwhitney/akka>`_
|
||||
Read more here: `<http://www.playframework.org/modules/akka>`_
|
||||
|
||||
Lift Web Framework
|
||||
==================
|
||||
|
||||
Home page: `<http://liftweb.net>`_
|
||||
|
||||
In order to use Akka with Lift you basically just have to do one thing, add the 'AkkaServlet' to your 'web.xml'.
|
||||
|
||||
web.xml
|
||||
-------
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<web-app>
|
||||
<!-- Akka specific stuff -->
|
||||
<servlet>
|
||||
<servlet-name>AkkaServlet</servlet-name>
|
||||
<servlet-class>akka.comet.AkkaServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>AkkaServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Lift specific stuff -->
|
||||
<filter>
|
||||
<filter-name>LiftFilter</filter-name>
|
||||
<display-name>Lift Filter</display-name>
|
||||
<description>The Filter that intercepts lift calls</description>
|
||||
<filter-class>net.liftweb.http.LiftFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>LiftFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
</web-app>
|
||||
|
||||
Boot class
|
||||
----------
|
||||
|
||||
Lift bootstrap happens in the Lift 'Boot' class. Here is a good place to add Akka specific initialization. For example add declarative supervisor configuration to wire up the initial Actors.
|
||||
Here is a full example taken from the Akka sample code, found here `<http://github.com/jboner/akka/tree/master/akka-samples/akka-sample-lift/>`_.
|
||||
|
||||
If a request is processed by Liftweb filter, Akka will not process the request. To disable processing of a request by the Lift filter :
|
||||
* append partial function to LiftRules.liftRequest and return *false* value to disable processing of matching request
|
||||
* use LiftRules.passNotFoundToChain to chain the request to the Akka filter
|
||||
|
||||
Example of Boot class source code :
|
||||
`<code format="scala">`_
|
||||
class Boot {
|
||||
def boot {
|
||||
// where to search snippet
|
||||
LiftRules.addToPackages("sample.lift")
|
||||
|
||||
LiftRules.httpAuthProtectedResource.prepend {
|
||||
case (ParsePath("liftpage" :: Nil, _, _, _)) => Full(AuthRole("admin"))
|
||||
}
|
||||
|
||||
LiftRules.authentication = HttpBasicAuthentication("lift") {
|
||||
case ("someuser", "1234", req) => {
|
||||
Log.info("You are now authenticated !")
|
||||
userRoles(AuthRole("admin"))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
LiftRules.liftRequest.append {
|
||||
case Req("liftcount" :: _, _, _) => false
|
||||
case Req("persistentliftcount" :: _, _, _) => false
|
||||
}
|
||||
LiftRules.passNotFoundToChain = true
|
||||
|
||||
// Akka supervisor configuration wiring up initial Actor services
|
||||
val supervisor = Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])),
|
||||
Supervise(
|
||||
actorOf[SimpleService],
|
||||
LifeCycle(Permanent)) ::
|
||||
Supervise(
|
||||
actorOf[PersistentSimpleService],
|
||||
LifeCycle(Permanent)) ::
|
||||
Nil))
|
||||
|
||||
// Build SiteMap
|
||||
// val entries = Menu(Loc("Home", List("index"), "Home")) :: Nil
|
||||
// LiftRules.setSiteMap(SiteMap(entries:_*))
|
||||
}
|
||||
}
|
||||
`<code>`_
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
Actors
|
||||
======
|
||||
Actors (Scala)
|
||||
==============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
|
|
@ -26,6 +30,9 @@ Here is an example:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.event.EventHandler
|
||||
|
||||
class MyActor extends Actor {
|
||||
def receive = {
|
||||
case "test" => EventHandler.info(this, "received test")
|
||||
|
|
@ -382,7 +389,7 @@ When you start the ``Actor`` then it will automatically call the ``def preStart`
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
override def preStart = {
|
||||
override def preStart() = {
|
||||
... // initialization code
|
||||
}
|
||||
|
||||
|
|
@ -399,7 +406,7 @@ When stop is called then a call to the ``def postStop`` callback method will tak
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
... // clean up resources
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
Agents (Scala)
|
||||
==============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Agents in Akka were inspired by `agents in Clojure <http://clojure.org/agents>`_.
|
||||
|
|
@ -88,6 +92,29 @@ Transactional Agents
|
|||
|
||||
If an Agent is used within an enclosing transaction, then it will participate in that transaction. If you send to an Agent within a transaction then the dispatch to the Agent will be held until that transaction commits, and discarded if the transaction is aborted.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.agent.Agent
|
||||
import akka.stm._
|
||||
|
||||
def transfer(from: Agent[Int], to: Agent[Int], amount: Int): Boolean = {
|
||||
atomic {
|
||||
if (from.get < amount) false
|
||||
else {
|
||||
from send (_ - amount)
|
||||
to send (_ + amount)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val from = Agent(100)
|
||||
val to = Agent(20)
|
||||
val ok = transfer(from, to, 50)
|
||||
|
||||
from() // -> 50
|
||||
to() // -> 70
|
||||
|
||||
Monadic usage
|
||||
-------------
|
||||
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
Dispatchers (Scala)
|
||||
===================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
The Dispatcher is an important piece that allows you to configure the right semantics and parameters for optimal performance, throughput and scalability. Different Actors have different needs.
|
||||
|
|
@ -124,7 +128,7 @@ If you don't define a the 'throughput' option in the configuration file then the
|
|||
Browse the `ScalaDoc <scaladoc>`_ or look at the code for all the options available.
|
||||
|
||||
Priority event-based
|
||||
^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply
|
||||
a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended):
|
||||
|
|
@ -231,11 +235,13 @@ For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingD
|
|||
For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor.
|
||||
Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout.
|
||||
|
||||
`<code format="scala">`_
|
||||
class MyActor extends Actor {
|
||||
import akka.util.duration._
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self, mailboxCapacity = 100,
|
||||
pushTimeOut = 10 seconds)
|
||||
...
|
||||
}
|
||||
`<code>`_
|
||||
.. code-block:: scala
|
||||
|
||||
class MyActor extends Actor {
|
||||
import akka.util.duration._
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self, mailboxCapacity = 100,
|
||||
pushTimeOut = 10 seconds)
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -49,13 +49,14 @@ Now lets create an object representing the FSM and defining the behavior.
|
|||
|
||||
import akka.actor.{Actor, FSM}
|
||||
import akka.event.EventHandler
|
||||
import FSM._
|
||||
import akka.util.duration._
|
||||
|
||||
case object Move
|
||||
|
||||
class ABC extends Actor with FSM[ExampleState, Unit] {
|
||||
|
||||
import FSM._
|
||||
|
||||
startWith(A, Unit)
|
||||
|
||||
when(A) {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,14 @@ Scala API
|
|||
:maxdepth: 2
|
||||
|
||||
actors
|
||||
typed-actors
|
||||
actor-registry
|
||||
agents
|
||||
stm
|
||||
transactors
|
||||
remote-actors
|
||||
serialization
|
||||
dispatchers
|
||||
fsm
|
||||
testing
|
||||
tutorial-chat-server
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
Remote Actors (Scala)
|
||||
=====================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Akka supports starting Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
Akka supports starting and interacting with Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
|
||||
The usage is completely transparent both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message.
|
||||
The usage is completely transparent with local actors, both in regards to sending messages and error handling and propagation as well as supervision, linking and restarts. You can send references to other Actors as part of the message.
|
||||
|
||||
You can find a runnable sample `here <http://github.com/jboner/akka/tree/master/akka-samples/akka-sample-remote/>`_.
|
||||
You can find a runnable sample `here <http://github.com/jboner/akka/tree/master/akka-samples/akka-sample-remote/>`__.
|
||||
|
||||
Starting up the remote service
|
||||
------------------------------
|
||||
|
|
@ -64,7 +68,7 @@ If you invoke 'shutdown' on the server then the connection will be closed.
|
|||
|
||||
import akka.actor.Actor._
|
||||
|
||||
remote.shutdown
|
||||
remote.shutdown()
|
||||
|
||||
Connecting and shutting down a client explicitly
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -74,6 +78,7 @@ Normally you should not have to start and stop the client connection explicitly
|
|||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor._
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
remote.shutdownClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise
|
||||
remote.restartClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise
|
||||
|
|
@ -143,12 +148,6 @@ The default behavior is that the remote client will maintain a transaction log o
|
|||
|
||||
If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown.
|
||||
|
||||
You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried).
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val pending: Array[Any] = Actor.remote.pendingMessages
|
||||
|
||||
Running Remote Server in untrusted mode
|
||||
---------------------------------------
|
||||
|
||||
|
|
@ -255,24 +254,16 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen
|
|||
|
||||
The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash.
|
||||
|
||||
Remote Actors
|
||||
-------------
|
||||
|
||||
Akka has two types of remote actors:
|
||||
|
||||
* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server.
|
||||
* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc.
|
||||
|
||||
Client-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
The client creates the remote actor and "moves it" to the server.
|
||||
|
||||
Actors can be made remote by calling remote().actorOf[MyActor](host, port)
|
||||
When you define an actor as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
|
||||
Actors can be made remote by calling remote.actorOf[MyActor](host, port)
|
||||
|
||||
Here is an example:
|
||||
|
||||
|
|
@ -280,29 +271,30 @@ Here is an example:
|
|||
|
||||
import akka.actor.Actor
|
||||
|
||||
class MyActor extends RemoteActor() {
|
||||
class MyActor extends Actor {
|
||||
def receive = {
|
||||
case "hello" => self.reply("world")
|
||||
}
|
||||
}
|
||||
|
||||
val remote = Actor.remote().actorOf[MyActor]("192.68.23.769", 2552)
|
||||
val remoteActor = Actor.remote.actorOf[MyActor]("192.68.23.769", 2552)
|
||||
|
||||
An Actor can also start remote child Actors through one of the 'spawn/link' methods. These will start, link and make the Actor remote atomically.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
...
|
||||
spawnRemote[MyActor](hostname, port)
|
||||
spawnLinkRemote[MyActor](hostname, port)
|
||||
self.spawnRemote[MyActor](hostname, port, timeout)
|
||||
self.spawnLinkRemote[MyActor](hostname, port, timeout)
|
||||
...
|
||||
|
||||
Server-managed Remote Actors
|
||||
----------------------------
|
||||
|
||||
Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
Server side setup
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote actors is really simple. 2 methods only:
|
||||
|
||||
.. code-block:: scala
|
||||
|
|
@ -332,7 +324,7 @@ Session bound server side setup
|
|||
Session bound server managed remote actors work by creating and starting a new actor for every client that connects. Actors are stopped automatically when the client disconnects. The client side is the same as regular server managed remote actors. Use the function registerPerSession instead of register.
|
||||
|
||||
Session bound actors are useful if you need to keep state per session, e.g. username.
|
||||
They are also useful if you need to perform some cleanup when a client disconnects by overriding the postStop method as described `here <actors-scala#Stopping actors>`_
|
||||
They are also useful if you need to perform some cleanup when a client disconnects by overriding the postStop method as described `here <actors-scala#Stopping actors>`__
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -358,10 +350,10 @@ There are many variations on the 'remote#actorFor' method. Here are some of them
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
... = actorFor(className, hostname, port)
|
||||
... = actorFor(className, timeout, hostname, port)
|
||||
... = actorFor(uuid, className, hostname, port)
|
||||
... = actorFor(uuid, className, timeout, hostname, port)
|
||||
... = remote.actorFor(className, hostname, port)
|
||||
... = remote.actorFor(className, timeout, hostname, port)
|
||||
... = remote.actorFor(uuid, className, hostname, port)
|
||||
... = remote.actorFor(uuid, className, timeout, hostname, port)
|
||||
... // etc
|
||||
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
|
@ -371,11 +363,16 @@ Running sample
|
|||
|
||||
Here is a complete running sample (also available `here <http://github.com/jboner/akka/blob/master/akka-core/src/test/scala/ServerInitiatedRemoteActorSample.scala>`_):
|
||||
|
||||
Paste in the code below into two sbt concole shells. Then run:
|
||||
|
||||
- ServerInitiatedRemoteActorServer.run() in one shell
|
||||
- ServerInitiatedRemoteActorClient.run() in the other shell
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.util.Logging
|
||||
import Actor._
|
||||
import akka.event.EventHandler
|
||||
|
||||
class HelloWorldActor extends Actor {
|
||||
def receive = {
|
||||
|
|
@ -385,27 +382,27 @@ Here is a complete running sample (also available `here <http://github.com/jbone
|
|||
|
||||
object ServerInitiatedRemoteActorServer {
|
||||
|
||||
def run = {
|
||||
def run() {
|
||||
remote.start("localhost", 2552)
|
||||
remote.register("hello-service", actorOf[HelloWorldActor])
|
||||
}
|
||||
|
||||
def main(args: Array[String]) = run
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
object ServerInitiatedRemoteActorClient extends Logging {
|
||||
object ServerInitiatedRemoteActorClient {
|
||||
|
||||
def run = {
|
||||
def run() {
|
||||
val actor = remote.actorFor("hello-service", "localhost", 2552)
|
||||
val result = actor !! "Hello"
|
||||
log.info("Result from Remote Actor: %s", result)
|
||||
EventHandler.info("Result from Remote Actor: %s", result)
|
||||
}
|
||||
|
||||
def main(args: Array[String]) = run
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
Automatic remote 'sender' reference management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------------------
|
||||
|
||||
The sender of a remote message will be reachable with a reply through the remote server on the node that the actor is residing, automatically.
|
||||
Please note that firewalled clients won't work right now. [2011-01-05]
|
||||
|
|
@ -419,10 +416,10 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.util.UUID
|
||||
import akka.actor.newUuid
|
||||
|
||||
class MyActor extends Actor {
|
||||
self.id = UUID.newUuid.toString
|
||||
self.id = newUuid.toString
|
||||
def receive = {
|
||||
case "hello" => self.reply("world")
|
||||
}
|
||||
|
|
@ -430,11 +427,9 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
val actor = remote.actorOf[MyActor]("192.68.23.769", 2552)
|
||||
|
||||
Remote Typed Actors
|
||||
-------------------
|
||||
|
||||
Client-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Client-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
|
|
@ -458,13 +453,13 @@ You can also define an Typed Actor to be remote programmatically when creating i
|
|||
|
||||
... // use pojo as usual
|
||||
|
||||
Server-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Server-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor
|
||||
|
||||
|
|
@ -507,20 +502,20 @@ They are also useful if you need to perform some cleanup when a client disconnec
|
|||
Note that the second argument in registerTypedPerSessionActor is an implicit function. It will be called to create an actor every time a session is established.
|
||||
|
||||
Client side usage
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val actor = remote.typedActorFor(classOf[RegistrationService], "user-service", 5000L, "localhost", 2552)
|
||||
actor.registerUser(…)
|
||||
|
||||
There are variations on the 'RemoteClient#typedActorFor' method. Here are some of them:
|
||||
There are variations on the 'remote#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port)
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port)
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader)
|
||||
|
||||
Data Compression Configuration
|
||||
------------------------------
|
||||
|
|
@ -583,15 +578,19 @@ So a simple listener actor can look like this:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Actor._
|
||||
import akka.remoteinterface._
|
||||
|
||||
val listener = actorOf(new Actor {
|
||||
def receive = {
|
||||
case RemoteClientError(cause, client, address) => ... // act upon error
|
||||
case RemoteClientDisconnected(client, address) => ... // act upon disconnection
|
||||
case RemoteClientConnected(client, address) => ... // act upon connection
|
||||
case RemoteClientStarted(client, address) => ... // act upon client shutdown
|
||||
case RemoteClientShutdown(client, address) => ... // act upon client shutdown
|
||||
case RemoteClientWriteFailed(request, cause, client, address) => ... // act upon write failure
|
||||
case _ => //ignore other
|
||||
case RemoteClientError(cause, client, address) => //... act upon error
|
||||
case RemoteClientDisconnected(client, address) => //... act upon disconnection
|
||||
case RemoteClientConnected(client, address) => //... act upon connection
|
||||
case RemoteClientStarted(client, address) => //... act upon client shutdown
|
||||
case RemoteClientShutdown(client, address) => //... act upon client shutdown
|
||||
case RemoteClientWriteFailed(request, cause, client, address) => //... act upon write failure
|
||||
case _ => // ignore other
|
||||
}
|
||||
}).start()
|
||||
|
||||
|
|
@ -637,15 +636,19 @@ So a simple listener actor can look like this:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Actor._
|
||||
import akka.remoteinterface._
|
||||
|
||||
val listener = actorOf(new Actor {
|
||||
def receive = {
|
||||
case RemoteServerStarted(server) => ... // act upon server start
|
||||
case RemoteServerShutdown(server) => ... // act upon server shutdown
|
||||
case RemoteServerError(cause, server) => ... // act upon server error
|
||||
case RemoteServerClientConnected(server, clientAddress) => ... // act upon client connection
|
||||
case RemoteServerClientDisconnected(server, clientAddress) => ... // act upon client disconnection
|
||||
case RemoteServerClientClosed(server, clientAddress) => ... // act upon client connection close
|
||||
case RemoteServerWriteFailed(request, cause, server, clientAddress) => ... // act upon server write failure
|
||||
case RemoteServerStarted(server) => //... act upon server start
|
||||
case RemoteServerShutdown(server) => //... act upon server shutdown
|
||||
case RemoteServerError(cause, server) => //... act upon server error
|
||||
case RemoteServerClientConnected(server, clientAddress) => //... act upon client connection
|
||||
case RemoteServerClientDisconnected(server, clientAddress) => //... act upon client disconnection
|
||||
case RemoteServerClientClosed(server, clientAddress) => //... act upon client connection close
|
||||
case RemoteServerWriteFailed(request, cause, server, clientAddress) => //... act upon server write failure
|
||||
}
|
||||
}).start()
|
||||
|
||||
|
|
@ -662,7 +665,7 @@ Message Serialization
|
|||
|
||||
All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization.
|
||||
|
||||
Here are some examples, but full documentation can be found in the `Serialization section <serialization>`_.
|
||||
Here are some examples, but full documentation can be found in the :ref:`serialization-scala`.
|
||||
|
||||
Scala JSON
|
||||
^^^^^^^^^^
|
||||
|
|
@ -676,7 +679,7 @@ Protobuf
|
|||
|
||||
Protobuf message specification needs to be compiled with 'protoc' compiler.
|
||||
|
||||
.. code-block:: scala
|
||||
::
|
||||
|
||||
message ProtobufPOJO {
|
||||
required uint64 id = 1;
|
||||
|
|
@ -697,26 +700,27 @@ Using the generated message builder to send the message to a remote actor:
|
|||
SBinary
|
||||
^^^^^^^
|
||||
|
||||
`<code format="scala">`_
|
||||
case class User(firstNameLastName: Tuple2[String, String], email: String, age: Int) extends Serializable.SBinary[User] {
|
||||
import sbinary.DefaultProtocol._
|
||||
.. code-block:: scala
|
||||
|
||||
def this() = this(null, null, 0)
|
||||
case class User(firstNameLastName: Tuple2[String, String], email: String, age: Int) extends Serializable.SBinary[User] {
|
||||
import sbinary.DefaultProtocol._
|
||||
|
||||
implicit object UserFormat extends Format[User] {
|
||||
def reads(in : Input) = User(
|
||||
read[Tuple2[String, String]](in),
|
||||
read[String](in),
|
||||
read[Int](in))
|
||||
def writes(out: Output, value: User) = {
|
||||
write[Tuple2[String, String]](out, value. firstNameLastName)
|
||||
write[String](out, value.email)
|
||||
write[Int](out, value.age)
|
||||
def this() = this(null, null, 0)
|
||||
|
||||
implicit object UserFormat extends Format[User] {
|
||||
def reads(in : Input) = User(
|
||||
read[Tuple2[String, String]](in),
|
||||
read[String](in),
|
||||
read[Int](in))
|
||||
def writes(out: Output, value: User) = {
|
||||
write[Tuple2[String, String]](out, value. firstNameLastName)
|
||||
write[String](out, value.email)
|
||||
write[Int](out, value.age)
|
||||
}
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes)
|
||||
|
||||
def toBytes: Array[Byte] = toByteArray(this)
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes)
|
||||
|
||||
def toBytes: Array[Byte] = toByteArray(this)
|
||||
}
|
||||
`<code>`_
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
.. _serialization-scala:
|
||||
|
||||
Serialization (Scala)
|
||||
=====================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Serialization of ActorRef
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
An Actor can be serialized in two different ways:
|
||||
|
||||
|
|
@ -13,7 +19,7 @@ An Actor can be serialized in two different ways:
|
|||
|
||||
Both of these can be sent as messages over the network and/or store them to disk, in a persistent storage backend etc.
|
||||
|
||||
Actor serialization in Akka is implemented through a type class 'Format[T <: Actor]' which publishes the 'fromBinary' and 'toBinary' methods for serialization. Here's the complete definition of the type class:
|
||||
Actor serialization in Akka is implemented through a type class ``Format[T <: Actor]`` which publishes the ``fromBinary`` and ``toBinary`` methods for serialization. Here's the complete definition of the type class:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -31,15 +37,14 @@ Actor serialization in Akka is implemented through a type class 'Format[T <: Act
|
|||
// client needs to implement Format[] for the respective actor
|
||||
trait Format[T <: Actor] extends FromBinary[T] with ToBinary[T]
|
||||
|
||||
**Deep serialization of an Actor and ActorRef**
|
||||
-----------------------------------------------
|
||||
Deep serialization of an Actor and ActorRef
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can serialize the whole actor deeply, e.g. both the 'ActorRef' and then instance of its 'Actor'. This can be useful if you want to move an actor from one node to another, or if you want to store away an actor, with its state, into a database.
|
||||
You can serialize the whole actor deeply, e.g. both the ``ActorRef`` and then instance of its ``Actor``. This can be useful if you want to move an actor from one node to another, or if you want to store away an actor, with its state, into a database.
|
||||
|
||||
Here is an example of how to serialize an Actor.
|
||||
|
||||
Step 1: Define the actor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -53,11 +58,15 @@ Step 1: Define the actor
|
|||
}
|
||||
}
|
||||
|
||||
Step 2: Implement the type class for the actor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Step 2: Implement the type class for the actor. ProtobufProtocol.Counter is something you need to define yourself, as
|
||||
explained in the Protobuf section.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.{Serializer, Format}
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Actor._
|
||||
|
||||
object BinaryFormatMyActor {
|
||||
implicit object MyActorFormat extends Format[MyActor] {
|
||||
def fromBinary(bytes: Array[Byte], act: MyActor) = {
|
||||
|
|
@ -66,13 +75,11 @@ Step 2: Implement the type class for the actor
|
|||
act
|
||||
}
|
||||
def toBinary(ac: MyActor) =
|
||||
ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray
|
||||
}
|
||||
ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray
|
||||
}
|
||||
}
|
||||
|
||||
Step 3: Import the type class module definition and serialize / de-serialize
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -90,7 +97,8 @@ Step 3: Import the type class module definition and serialize / de-serialize
|
|||
(actor2 !! "hello").getOrElse("_") should equal("world 3")
|
||||
}
|
||||
|
||||
**Helper Type Class for Stateless Actors**
|
||||
Helper Type Class for Stateless Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If your actor is stateless, then you can use the helper trait that Akka provides to serialize / de-serialize. Here's the definition:
|
||||
|
||||
|
|
@ -138,9 +146,10 @@ and use it for serialization:
|
|||
(actor2 !! "hello").getOrElse("_") should equal("world")
|
||||
}
|
||||
|
||||
**Helper Type Class for actors with external serializer**
|
||||
Helper Type Class for actors with external serializer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use the trait 'SerializerBasedActorFormat' for specifying serializers.
|
||||
Use the trait ``SerializerBasedActorFormat`` for specifying serializers.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -154,7 +163,7 @@ For a Java serializable actor:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
@serializable class MyJavaSerializableActor extends Actor {
|
||||
class MyJavaSerializableActor extends Actor with scala.Serializable {
|
||||
var count = 0
|
||||
|
||||
def receive = {
|
||||
|
|
@ -168,6 +177,8 @@ Create a module for the type class ..
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.{SerializerBasedActorFormat, Serializer}
|
||||
|
||||
object BinaryFormatMyJavaSerializableActor {
|
||||
implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] {
|
||||
val serializer = Serializer.Java
|
||||
|
|
@ -179,6 +190,7 @@ and serialize / de-serialize ..
|
|||
.. code-block:: scala
|
||||
|
||||
it("should be able to serialize and de-serialize a stateful actor with a given serializer") {
|
||||
import akka.actor.Actor._
|
||||
import akka.serialization.ActorSerialization._
|
||||
import BinaryFormatMyJavaSerializableActor._
|
||||
|
||||
|
|
@ -192,14 +204,14 @@ and serialize / de-serialize ..
|
|||
(actor2 !! "hello").getOrElse("_") should equal("world 3")
|
||||
}
|
||||
|
||||
**Serialization of a RemoteActorRef**
|
||||
-------------------------------------
|
||||
Serialization of a RemoteActorRef
|
||||
---------------------------------
|
||||
|
||||
You can serialize an 'ActorRef' to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected.
|
||||
You can serialize an ``ActorRef`` to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected.
|
||||
|
||||
The 'RemoteActorRef' serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any 'ActorRef' (as long as the actor has **not** implemented one of the 'SerializableActor' traits, since then deep serialization will happen).
|
||||
The ``RemoteActorRef`` serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any ``ActorRef``.
|
||||
|
||||
Currently Akka will **not** autodetect an 'ActorRef' as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms.
|
||||
Currently Akka will **not** autodetect an ``ActorRef`` as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms.
|
||||
|
||||
Here is an example of how to serialize an Actor.
|
||||
|
||||
|
|
@ -209,34 +221,34 @@ Here is an example of how to serialize an Actor.
|
|||
|
||||
val bytes = toBinary(actor1)
|
||||
|
||||
To deserialize the 'ActorRef' to a 'RemoteActorRef' you need to use the 'fromBinaryToRemoteActorRef(bytes: Array[Byte])' method on the 'ActorRef' companion object:
|
||||
To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fromBinaryToRemoteActorRef(bytes: Array[Byte])`` method on the ``ActorRef`` companion object:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import RemoteActorSerialization._
|
||||
import akka.serialization.RemoteActorSerialization._
|
||||
val actor2 = fromBinaryToRemoteActorRef(bytes)
|
||||
|
||||
You can also pass in a class loader to load the 'ActorRef' class and dependencies from:
|
||||
You can also pass in a class loader to load the ``ActorRef`` class and dependencies from:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import RemoteActorSerialization._
|
||||
import akka.serialization.RemoteActorSerialization._
|
||||
val actor2 = fromBinaryToRemoteActorRef(bytes, classLoader)
|
||||
|
||||
Deep serialization of a TypedActor
|
||||
----------------------------------
|
||||
|
||||
Serialization of typed actors works almost the same way as untyped actors. You can serialize the whole actor deeply, e.g. both the 'proxied ActorRef' and the instance of its 'TypedActor'.
|
||||
Serialization of typed actors works almost the same way as untyped actors. You can serialize the whole actor deeply, e.g. both the 'proxied ActorRef' and the instance of its ``TypedActor``.
|
||||
|
||||
Here is the example from above implemented as a TypedActor.
|
||||
|
||||
^
|
||||
|
||||
Step 1: Define the actor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.TypedActor
|
||||
|
||||
trait MyTypedActor {
|
||||
def requestReply(s: String) : String
|
||||
def oneWay() : Unit
|
||||
|
|
@ -249,13 +261,18 @@ Step 1: Define the actor
|
|||
count = count + 1
|
||||
"world " + count
|
||||
}
|
||||
|
||||
override def oneWay() {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
|
||||
Step 2: Implement the type class for the actor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.{Serializer, Format}
|
||||
|
||||
class MyTypedActorFormat extends Format[MyTypedActorImpl] {
|
||||
def fromBinary(bytes: Array[Byte], act: MyTypedActorImpl) = {
|
||||
val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.Counter])).asInstanceOf[ProtobufProtocol.Counter]
|
||||
|
|
@ -266,10 +283,11 @@ Step 2: Implement the type class for the actor
|
|||
}
|
||||
|
||||
Step 3: Import the type class module definition and serialize / de-serialize
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.TypedActorSerialization._
|
||||
|
||||
val typedActor1 = TypedActor.newInstance(classOf[MyTypedActor], classOf[MyTypedActorImpl], 1000)
|
||||
|
||||
val f = new MyTypedActorFormat
|
||||
|
|
@ -278,23 +296,23 @@ Step 3: Import the type class module definition and serialize / de-serialize
|
|||
val typedActor2: MyTypedActor = fromBinaryJ(bytes, f) //type hint needed
|
||||
typedActor2.requestReply("hello")
|
||||
|
||||
-
|
||||
|
||||
|
||||
Serialization of a remote typed ActorRef
|
||||
----------------------------------------
|
||||
|
||||
To deserialize the TypedActor to a 'RemoteTypedActorRef' (an aspectwerkz proxy to a RemoteActorRef) you need to use the 'fromBinaryToRemoteTypedActorRef(bytes: Array[Byte])' method on 'RemoteTypedActorSerialization' object:
|
||||
To deserialize the TypedActor to a ``RemoteTypedActorRef`` (an aspectwerkz proxy to a RemoteActorRef) you need to use the ``fromBinaryToRemoteTypedActorRef(bytes: Array[Byte])`` method on ``RemoteTypedActorSerialization`` object:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import RemoteTypedActorSerialization._
|
||||
import akka.serialization.RemoteTypedActorSerialization._
|
||||
val typedActor = fromBinaryToRemoteTypedActorRef(bytes)
|
||||
|
||||
// you can also pass in a class loader
|
||||
val typedActor2 = fromBinaryToRemoteTypedActorRef(bytes, classLoader)
|
||||
|
||||
Compression
|
||||
===========
|
||||
-----------
|
||||
|
||||
Akka has a helper class for doing compression of binary data. This can be useful for example when storing data in one of the backing storages. It currently supports LZF which is a very fast compression algorithm suited for runtime dynamic compression.
|
||||
|
||||
|
|
@ -309,24 +327,27 @@ Here is an example of how it can be used:
|
|||
val uncompressBytes = Compression.LZF.uncompress(compressBytes)
|
||||
|
||||
Using the Serializable trait and Serializer class for custom serialization
|
||||
==========================================================================
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
If you are sending messages to a remote Actor and these messages implement one of the predefined interfaces/traits in the 'akka.serialization.Serializable.*' object, then Akka will transparently detect which serialization format it should use as wire protocol and will automatically serialize and deserialize the message according to this protocol.
|
||||
If you are sending messages to a remote Actor and these messages implement one of the predefined interfaces/traits in the ``akka.serialization.Serializable.*`` object, then Akka will transparently detect which serialization format it should use as wire protocol and will automatically serialize and deserialize the message according to this protocol.
|
||||
|
||||
Each serialization interface/trait in
|
||||
* akka.serialization.Serializable.*
|
||||
> has a matching serializer in
|
||||
* akka.serialization.Serializer.*
|
||||
|
||||
- akka.serialization.Serializable.*
|
||||
|
||||
has a matching serializer in
|
||||
|
||||
- akka.serialization.Serializer.*
|
||||
|
||||
Note however that if you are using one of the Serializable interfaces then you don’t have to do anything else in regard to sending remote messages.
|
||||
|
||||
The ones currently supported are (besides the default which is regular Java serialization):
|
||||
* ScalaJSON (Scala only)
|
||||
* JavaJSON (Java but some Scala structures)
|
||||
* SBinary (Scala only)
|
||||
* Protobuf (Scala and Java)
|
||||
|
||||
Apart from the above, Akka also supports Scala object serialization through `SJSON <http://github.com/debasishg/sjson/tree/master>`_ that implements APIs similar to 'akka.serialization.Serializer.*'. See the section on SJSON below for details.
|
||||
- ScalaJSON (Scala only)
|
||||
- JavaJSON (Java but some Scala structures)
|
||||
- Protobuf (Scala and Java)
|
||||
|
||||
Apart from the above, Akka also supports Scala object serialization through `SJSON <http://github.com/debasishg/sjson/tree/master>`_ that implements APIs similar to ``akka.serialization.Serializer.*``. See the section on SJSON below for details.
|
||||
|
||||
Protobuf
|
||||
--------
|
||||
|
|
@ -372,15 +393,16 @@ The remote Actor can then receive the Protobuf message typed as-is:
|
|||
JSON: Scala
|
||||
-----------
|
||||
|
||||
Use the akka.serialization.Serialization.ScalaJSON base class with its toJSON method. Akka’s Scala JSON is based upon the SJSON library.
|
||||
Use the ``akka.serialization.Serializable.ScalaJSON`` base class with its toJSON method. Akka’s Scala JSON is based upon the SJSON library.
|
||||
|
||||
For your POJOs to be able to serialize themselves you have to extend the ScalaJSON[] trait as follows. JSON serialization is based on a type class protocol which you need to define for your own abstraction. The instance of the type class is defined as an implicit object which is used for serialization and de-serialization. You also need to implement the methods in terms of the APIs which sjson publishes.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.Serializer
|
||||
import akka.serialization._
|
||||
import akka.serialization.Serializable.ScalaJSON
|
||||
import scala.reflect.BeanInfo
|
||||
import akka.serialization.JsonSerialization._
|
||||
import akka.serialization.DefaultProtocol._
|
||||
|
||||
case class MyMessage(val id: String, val value: Tuple2[String, Int]) extends ScalaJSON[MyMessage] {
|
||||
// type class instance
|
||||
|
|
@ -422,7 +444,7 @@ Here are the steps that you need to follow:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import DefaultProtocol._
|
||||
import akka.serialization.DefaultProtocol._
|
||||
implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
|
||||
asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
|
||||
|
||||
|
|
@ -431,6 +453,7 @@ Here are the steps that you need to follow:
|
|||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.Serializer.ScalaJSON
|
||||
import akka.serialization.JsonSerialization._
|
||||
|
||||
val o = MyMessage("dg", ("akka", 100))
|
||||
fromjson[MyMessage](tojson(o)) should equal(o)
|
||||
|
|
@ -475,14 +498,14 @@ So if you see something like that:
|
|||
|
||||
it means, that you haven't got a @BeanInfo annotation on your class.
|
||||
|
||||
You may also see this exception when trying to serialize a case class with out an attribute like this:
|
||||
You may also see this exception when trying to serialize a case class without any attributes, like this:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
@BeanInfo case class Empty() // cannot be serialized
|
||||
|
||||
SJSON: Scala
|
||||
-------------
|
||||
SJSON: Scala
|
||||
------------
|
||||
|
||||
SJSON supports serialization of Scala objects into JSON. It implements support for built in Scala structures like List, Map or String as well as custom objects. SJSON is available as an Apache 2 licensed project on Github `here <http://github.com/debasishg/sjson/tree/master>`_.
|
||||
|
||||
|
|
@ -535,7 +558,7 @@ What you get back from is a JsValue, an abstraction of the JSON object model. Fo
|
|||
Serialization of Embedded Objects
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SJSON supports serialization of Scala objects that have other embedded objects. Suppose you have the following Scala classes .. Here Contact has an embedded Address Map ..
|
||||
SJSON supports serialization of Scala objects that have other embedded objects. Suppose you have the following Scala classes .. Here Contact has an embedded Address Map ..
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -593,7 +616,7 @@ With SJSON, I can do the following:
|
|||
"Market Street" should equal(
|
||||
(r ># { ('addresses ? obj) andThen ('residence ? obj) andThen ('street ? str) }))
|
||||
|
||||
^
|
||||
|
||||
|
||||
Changing property names during serialization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -619,7 +642,7 @@ When this will be serialized out, the property name will be changed.
|
|||
JsString("ISBN") -> JsString("012-456372")
|
||||
)
|
||||
|
||||
^
|
||||
|
||||
|
||||
Serialization with ignore properties
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -653,7 +676,7 @@ The annotation @JSONProperty can be used to selectively ignore fields. When I se
|
|||
|
||||
Similarly, we can ignore properties of an object **only** if they are null and not ignore otherwise. Just specify the annotation @JSONProperty as @JSONProperty {val ignoreIfNull = true}.
|
||||
|
||||
^
|
||||
|
||||
|
||||
Serialization with Type Hints for Generic Data Members
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -892,14 +915,18 @@ and the serialization in action in the REPL:
|
|||
|
||||
There are other nifty ways to implement case class serialization using sjson. For more details, have a look at the `wiki <http://wiki.github.com/debasishg/sjson/typeclass-based-json-serialization>`_ for sjson.
|
||||
|
||||
**<span class="caps" style="line-height: 1.4em; margin: 0px; padding: 0px;">JSON</span>: Java**
|
||||
JSON: Java
|
||||
----------
|
||||
|
||||
Use the akka.serialization.Serialization.JavaJSON base class with its toJSONmethod. Akka’s Java JSON is based upon the Jackson library.
|
||||
Use the ``akka.serialization.Serializable.JavaJSON`` base class with its toJSONmethod. Akka’s Java JSON is based upon the Jackson library.
|
||||
|
||||
For your POJOs to be able to serialize themselves you have to extend the JavaJSON trait.
|
||||
For your POJOs to be able to serialize themselves you have to extend the JavaJSON base class.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.serialization.Serializable.JavaJSON;
|
||||
import akka.serialization.SerializerFactory;
|
||||
|
||||
class MyMessage extends JavaJSON {
|
||||
private String name = null;
|
||||
public MyMessage(String name) {
|
||||
|
|
@ -924,55 +951,5 @@ Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSON seri
|
|||
String json = factory.getJavaJSON().out(foo);
|
||||
Foo fooCopy = factory.getJavaJSON().in(json, Foo.class);
|
||||
|
||||
-
|
||||
|
||||
SBinary: Scala
|
||||
--------------
|
||||
|
||||
To serialize Scala structures you can use SBinary serializer. SBinary can serialize all primitives and most default Scala datastructures; such as List, Tuple, Map, Set, BigInt etc.
|
||||
|
||||
Here is an example of using the akka.serialization.Serializer.SBinary serializer to serialize standard Scala library objects.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.serialization.Serializer
|
||||
import sbinary.DefaultProtocol._ // you always need to import these implicits
|
||||
val users = List(("user1", "passwd1"), ("user2", "passwd2"), ("user3", "passwd3"))
|
||||
val bytes = Serializer.SBinary.out(users)
|
||||
val usersCopy = Serializer.SBinary.in(bytes, Some(classOf[List[Tuple2[String,String]]]))
|
||||
|
||||
If you need to serialize your own user-defined objects then you have to do three things:
|
||||
# Define an empty constructor
|
||||
# Mix in the Serializable.SBinary[T] trait, and implement its methods:
|
||||
## fromBytes(bytes: Array[Byte])[T]
|
||||
## toBytes: Array[Byte]
|
||||
# Create an implicit sbinary.Format[T] object for your class. Which means that you have to define its two methods:
|
||||
## reads(in: Input): T; in which you read in all the fields in your object, using read[FieldType](in)and recreate it.
|
||||
## writes(out: Output, value: T): Unit; in which you write out all the fields in your object, using write[FieldType](out, value.field).
|
||||
|
||||
Here is an example:
|
||||
`<code format="scala">`_
|
||||
case class User(val usernamePassword: Tuple2[String, String], val email: String, val age: Int)
|
||||
extends Serializable.SBinary[User] {
|
||||
import sbinary.DefaultProtocol._
|
||||
import sbinary.Operations._
|
||||
|
||||
def this() = this(null, null, 0)
|
||||
|
||||
implicit object UserFormat extends Format[User] {
|
||||
def reads(in : Input) = User(
|
||||
read[Tuple2[String, String]](in),
|
||||
read[String](in),
|
||||
read[Int](in))
|
||||
def writes(out: Output, value: User) = {
|
||||
write[Tuple2[String, String]](out, value.usernamePassword)
|
||||
write[String](out, value.email)
|
||||
write[Int](out, value.age)
|
||||
}
|
||||
}
|
||||
|
||||
def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes)
|
||||
|
||||
def toBytes: Array[Byte] = toByteArray(this)
|
||||
}
|
||||
`<code>`_
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
Software Transactional Memory (Scala)
|
||||
=====================================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Overview of STM
|
||||
===============
|
||||
---------------
|
||||
|
||||
An `STM <http://en.wikipedia.org/wiki/Software_transactional_memory>`_ turns the Java heap into a transactional data set with begin/commit/rollback semantics. Very much like a regular database. It implements the first three letters in ACID; ACI:
|
||||
* Atomic
|
||||
|
|
@ -24,7 +28,7 @@ The STM is based on Transactional References (referred to as Refs). Refs are mem
|
|||
Working with immutable collections can sometimes give bad performance due to extensive copying. Scala provides so-called persistent datastructures which makes working with immutable collections fast. They are immutable but with constant time access and modification. They use structural sharing and an insert or update does not ruin the old structure, hence “persistent”. Makes working with immutable composite types fast. The persistent datastructures currently consist of a Map and Vector.
|
||||
|
||||
Simple example
|
||||
==============
|
||||
--------------
|
||||
|
||||
Here is a simple example of an incremental counter using STM. This shows creating a ``Ref``, a transactional reference, and then modifying it within a transaction, which is delimited by ``atomic``.
|
||||
|
||||
|
|
@ -44,15 +48,14 @@ Here is a simple example of an incremental counter using STM. This shows creatin
|
|||
counter
|
||||
// -> 2
|
||||
|
||||
----
|
||||
|
||||
Ref
|
||||
===
|
||||
---
|
||||
|
||||
Refs (transactional references) are mutable references to values and through the STM allow the safe sharing of mutable data. Refs separate identity from value. To ensure safety the value stored in a Ref should be immutable (they can of course contain refs themselves). The value referenced by a Ref can only be accessed or swapped within a transaction. If a transaction is not available, the call will be executed in its own transaction (the call will be atomic). This is a different approach than the Clojure Refs, where a missing transaction results in an error.
|
||||
|
||||
Creating a Ref
|
||||
--------------
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
You can create a Ref with or without an initial value.
|
||||
|
||||
|
|
@ -67,7 +70,7 @@ You can create a Ref with or without an initial value.
|
|||
val ref = Ref[Int]
|
||||
|
||||
Accessing the value of a Ref
|
||||
----------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use ``get`` to access the value of a Ref. Note that if no initial value has been given then the value is initially ``null``.
|
||||
|
||||
|
|
@ -97,7 +100,7 @@ If there is a chance that the value of a Ref is null then you can use ``opt``, w
|
|||
}
|
||||
|
||||
Changing the value of a Ref
|
||||
---------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To set a new value for a Ref you can use ``set`` (or equivalently ``swap``), which sets the new value and returns the old value.
|
||||
|
||||
|
|
@ -138,7 +141,7 @@ You can also use ``alter`` which accepts a function that takes the old value and
|
|||
// -> 6
|
||||
|
||||
Refs in for-comprehensions
|
||||
--------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Ref is monadic and can be used in for-comprehensions.
|
||||
|
||||
|
|
@ -174,10 +177,9 @@ Ref is monadic and can be used in for-comprehensions.
|
|||
}
|
||||
// -> Ref[Int]
|
||||
|
||||
----
|
||||
|
||||
Transactions
|
||||
============
|
||||
------------
|
||||
|
||||
A transaction is delimited using ``atomic``.
|
||||
|
||||
|
|
@ -190,24 +192,24 @@ A transaction is delimited using ``atomic``.
|
|||
All changes made to transactional objects are isolated from other changes, all make it or non make it (so failure atomicity) and are consistent. With the AkkaSTM you automatically have the Oracle version of the SERIALIZED isolation level, lower isolation is not possible. To make it fully serialized, set the writeskew property that checks if a writeskew problem is allowed to happen.
|
||||
|
||||
Retries
|
||||
-------
|
||||
^^^^^^^
|
||||
|
||||
A transaction is automatically retried when it runs into some read or write conflict, until the operation completes, an exception (throwable) is thrown or when there are too many retries. When a read or writeconflict is encountered, the transaction uses a bounded exponential backoff to prevent cause more contention and give other transactions some room to complete.
|
||||
|
||||
If you are using non transactional resources in an atomic block, there could be problems because a transaction can be retried. If you are using print statements or logging, it could be that they are called more than once. So you need to be prepared to deal with this. One of the possible solutions is to work with a deferred or compensating task that is executed after the transaction aborts or commits.
|
||||
|
||||
Unexpected retries
|
||||
------------------
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It can happen for the first few executions that you get a few failures of execution that lead to unexpected retries, even though there is not any read or writeconflict. The cause of this is that speculative transaction configuration/selection is used. There are transactions optimized for a single transactional object, for 1..n and for n to unlimited. So based on the execution of the transaction, the system learns; it begins with a cheap one and upgrades to more expensive ones. Once it has learned, it will reuse this knowledge. It can be activated/deactivated using the speculative property on the TransactionFactory. In most cases it is best use the default value (enabled) so you get more out of performance.
|
||||
|
||||
Coordinated transactions and Transactors
|
||||
----------------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you need coordinated transactions across actors or threads then see `Transactors <transactors-scala>`_.
|
||||
If you need coordinated transactions across actors or threads then see :ref:`transactors-scala`.
|
||||
|
||||
Configuring transactions
|
||||
------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It's possible to configure transactions. The ``atomic`` method can take an implicit or explicit ``TransactionFactory``, which can determine properties of the transaction. A default transaction factory is used if none is specified explicitly or there is no implicit ``TransactionFactory`` in scope.
|
||||
|
||||
|
|
@ -311,7 +313,7 @@ Here's a similar example with an individual transaction factory for each instanc
|
|||
}
|
||||
|
||||
Transaction lifecycle listeners
|
||||
-------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It's possible to have code that will only run on the successful commit of a transaction, or when a transaction aborts. You can do this by adding ``deferred`` or ``compensating`` blocks to a transaction.
|
||||
|
||||
|
|
@ -329,7 +331,7 @@ It's possible to have code that will only run on the successful commit of a tran
|
|||
}
|
||||
|
||||
Blocking transactions
|
||||
---------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can block in a transaction until a condition is met by using an explicit ``retry``. To use ``retry`` you also need to configure the transaction to allow explicit retries.
|
||||
|
||||
|
|
@ -383,7 +385,7 @@ Here is an example of using ``retry`` to block until an account has enough money
|
|||
transferer.stop()
|
||||
|
||||
Alternative blocking transactions
|
||||
---------------------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can also have two alternative blocking transactions, one of which can succeed first, with ``either-orElse``.
|
||||
|
||||
|
|
@ -434,10 +436,9 @@ You can also have two alternative blocking transactions, one of which can succee
|
|||
|
||||
brancher.stop()
|
||||
|
||||
----
|
||||
|
||||
Transactional datastructures
|
||||
============================
|
||||
----------------------------
|
||||
|
||||
Akka provides two datastructures that are managed by the STM.
|
||||
|
||||
|
|
@ -511,26 +512,23 @@ Here is the same example using TransactionalMap:
|
|||
}
|
||||
// -> User("bill")
|
||||
|
||||
----
|
||||
|
||||
Persistent datastructures
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
Akka's STM should only be used with immutable data. This can be costly if you have large datastructures and are using a naive copy-on-write. In order to make working with immutable datastructures fast enough Scala provides what are called Persistent Datastructures. There are currently two different ones:
|
||||
* HashMap (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/HashMap.html>`_)
|
||||
* Vector (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html>`_)
|
||||
* HashMap (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/HashMap.html>`__)
|
||||
* Vector (`scaladoc <http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html>`__)
|
||||
|
||||
They are immutable and each update creates a completely new version but they are using clever structural sharing in order to make them almost as fast, for both read and update, as regular mutable datastructures.
|
||||
|
||||
This illustration is taken from Rich Hickey's presentation. Copyright Rich Hickey 2009.
|
||||
|
||||
.. image:: http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png
|
||||
.. image:: ../images/clojure-trees.png
|
||||
|
||||
|
||||
----
|
||||
|
||||
JTA integration
|
||||
===============
|
||||
---------------
|
||||
|
||||
The STM has JTA (Java Transaction API) integration. This means that it will, if enabled, hook in to JTA and start a JTA transaction when the STM transaction is started. It will also rollback the STM transaction if the JTA transaction has failed and vice versa. This does not mean that the STM is made durable, if you need that you should use one of the `persistence modules <persistence>`_. It simply means that the STM will participate and interact with and external JTA provider, for example send a message using JMS atomically within an STM transaction, or use Hibernate to persist STM managed data etc.
|
||||
|
||||
|
|
@ -556,9 +554,8 @@ You also have to configure which JTA provider to use etc in the 'jta' config sec
|
|||
timeout = 60
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Ants simulation sample
|
||||
======================
|
||||
----------------------
|
||||
|
||||
One fun and very enlightening visual demo of STM, actors and transactional references is the `Ant simulation sample <http://github.com/jboner/akka/tree/master/akka-samples/akka-sample-ants/>`_. I encourage you to run it and read through the code since it's a good example of using actors with STM.
|
||||
|
|
@ -40,6 +40,10 @@ encompass functional tests of complete actor networks. The important
|
|||
distinction lies in whether concurrency concerns are part of the test or not.
|
||||
The tools offered are described in detail in the following sections.
|
||||
|
||||
.. note::
|
||||
|
||||
Be sure to add the module :mod:`akka-testkit` to your dependencies.
|
||||
|
||||
Unit Testing with :class:`TestActorRef`
|
||||
=======================================
|
||||
|
||||
|
|
@ -68,6 +72,8 @@ reference is done like this:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.testkit.TestActorRef
|
||||
|
||||
val actorRef = TestActorRef[MyActor]
|
||||
val actor = actorRef.underlyingActor
|
||||
|
||||
|
|
@ -169,6 +175,10 @@ common task easy:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.testkit.TestKit
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class MySpec extends WordSpec with MustMatchers with TestKit {
|
||||
|
||||
"An Echo actor" must {
|
||||
|
|
@ -252,6 +262,31 @@ runs everything which would normally be queued directly on the current thread,
|
|||
the full history of a message's processing chain is recorded on the call stack,
|
||||
so long as all intervening actors run on this dispatcher.
|
||||
|
||||
How to use it
|
||||
-------------
|
||||
|
||||
Just set the dispatcher as you normally would, either from within the actor
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.testkit.CallingThreadDispatcher
|
||||
|
||||
class MyActor extends Actor {
|
||||
self.dispatcher = CallingThreadDispatcher.global
|
||||
...
|
||||
}
|
||||
|
||||
or from the client code
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val ref = Actor.actorOf[MyActor]
|
||||
ref.dispatcher = CallingThreadDispatcher.global
|
||||
ref.start()
|
||||
|
||||
As the :class:`CallingThreadDispatcher` does not have any configurable state,
|
||||
you may always use the (lazily) preallocated one as shown in the examples.
|
||||
|
||||
How it works
|
||||
------------
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
TestKit Example
|
||||
###############
|
||||
|
||||
Ray Roestenburg's example code from `his blog <http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html>`_.
|
||||
Ray Roestenburg's example code from `his blog <http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html>`_ adapted to work with Akka 1.1.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ Ray Roestenburg's example code from `his blog <http://roestenburg.agilesquad.com
|
|||
import org.scalatest.{WordSpec, BeforeAndAfterAll}
|
||||
import akka.actor.Actor._
|
||||
import akka.util.duration._
|
||||
import akka.util.TestKit
|
||||
import akka.testkit.TestKit
|
||||
import java.util.concurrent.TimeUnit
|
||||
import akka.actor.{ActorRef, Actor}
|
||||
import util.Random
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
**<span style="font-size: 150%;">Transactors (Scala)</span>**
|
||||
=============================================================
|
||||
.. _transactors-scala:
|
||||
|
||||
Transactors (Scala)
|
||||
===================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Why Transactors?
|
||||
================
|
||||
----------------
|
||||
|
||||
Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each operation needs to be atomic. For detailed discussion on the topic see `this JavaOne presentation <http://www.slideshare.net/jboner/state-youre-doing-it-wrong-javaone-2009>`_.
|
||||
|
||||
|
|
@ -15,21 +21,21 @@ Akka's Transactors combine Actors and STM to provide the best of the Actor model
|
|||
If you need Durability then you should not use one of the in-memory data structures but one of the persistent ones.
|
||||
|
||||
Generally, the STM is not needed very often when working with Akka. Some use-cases (that we can think of) are:
|
||||
# When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it.
|
||||
# When you want to share a datastructure across actors.
|
||||
# When you need to use the persistence modules.
|
||||
|
||||
- When you really need composable message flows across many actors updating their **internal local** state but need them to do that atomically in one big transaction. Might not often, but when you do need this then you are screwed without it.
|
||||
- When you want to share a datastructure across actors.
|
||||
- When you need to use the persistence modules.
|
||||
|
||||
Actors and STM
|
||||
--------------
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
You can combine Actors and STM in several ways. An Actor may use STM internally so that particular changes are guaranteed to be atomic. Actors may also share transactional datastructures as the STM provides safe shared state across threads.
|
||||
|
||||
It's also possible to coordinate transactions across Actors or threads so that either the transactions in a set all commit successfully or they all fail. This is the focus of Transactors and the explicit support for coordinated transactions in this section.
|
||||
|
||||
----
|
||||
|
||||
Coordinated transactions
|
||||
========================
|
||||
------------------------
|
||||
|
||||
Akka provides an explicit mechanism for coordinating transactions across Actors. Under the hood it uses a ``CountDownCommitBarrier``, similar to a CountDownLatch.
|
||||
|
||||
|
|
@ -70,7 +76,7 @@ Here is an example of coordinating two simple counter Actors so that they both i
|
|||
counter1.stop()
|
||||
counter2.stop()
|
||||
|
||||
To start a new coordinated transaction set that you will also participate in, just create a ``Coordinated`` object:
|
||||
To start a new coordinated transaction that you will also participate in, just create a ``Coordinated`` object:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -90,7 +96,7 @@ To receive a coordinated message in an actor simply match it in a case statement
|
|||
case coordinated @ Coordinated(Message) => ...
|
||||
}
|
||||
|
||||
To include another actor in the same coordinated transaction set that you've created or received, use the apply method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent.
|
||||
To include another actor in the same coordinated transaction that you've created or received, use the apply method on that object. This will increment the number of parties involved by one and create a new ``Coordinated`` object to be sent.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
|
|
@ -106,10 +112,9 @@ To enter the coordinated transaction use the atomic method of the coordinated ob
|
|||
|
||||
The coordinated transaction will wait for the other transactions before committing. If any of the coordinated transactions fail then they all fail.
|
||||
|
||||
----
|
||||
|
||||
Transactor
|
||||
==========
|
||||
----------
|
||||
|
||||
Transactors are actors that provide a general pattern for coordinating transactions, using the explicit coordination described above.
|
||||
|
||||
|
|
@ -125,7 +130,7 @@ Here's an example of a simple transactor that will join a coordinated transactio
|
|||
class Counter extends Transactor {
|
||||
val count = Ref(0)
|
||||
|
||||
def atomically = {
|
||||
override def atomically = {
|
||||
case Increment => count alter (_ + 1)
|
||||
}
|
||||
}
|
||||
|
|
@ -140,6 +145,7 @@ Example of coordinating an increment:
|
|||
|
||||
import akka.transactor.Transactor
|
||||
import akka.stm.Ref
|
||||
import akka.actor.ActorRef
|
||||
|
||||
case object Increment
|
||||
|
||||
|
|
@ -150,7 +156,7 @@ Example of coordinating an increment:
|
|||
case Increment => include(friend)
|
||||
}
|
||||
|
||||
def atomically = {
|
||||
override def atomically = {
|
||||
case Increment => count alter (_ + 1)
|
||||
}
|
||||
}
|
||||
|
|
@ -176,10 +182,9 @@ To execute directly before or after the coordinated transaction, override the ``
|
|||
|
||||
To completely bypass coordinated transactions override the ``normally`` method. Any message matched by ``normally`` will not be matched by the other methods, and will not be involved in coordinated transactions. In this method you can implement normal actor behavior, or use the normal STM atomic for local transactions.
|
||||
|
||||
----
|
||||
|
||||
Coordinating Typed Actors
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
It's also possible to use coordinated transactions with typed actors. You can explicitly pass around ``Coordinated`` objects, or use built-in support with the ``@Coordinated`` annotation and the ``Coordination.coordinate`` method.
|
||||
|
||||
|
|
@ -188,7 +193,7 @@ To specify a method should use coordinated transactions add the ``@Coordinated``
|
|||
.. code-block:: scala
|
||||
|
||||
trait Counter {
|
||||
@Coordinated def increment: Unit
|
||||
@Coordinated def increment()
|
||||
def get: Int
|
||||
}
|
||||
|
||||
|
|
@ -197,8 +202,8 @@ To coordinate transactions use a ``coordinate`` block:
|
|||
.. code-block:: scala
|
||||
|
||||
coordinate {
|
||||
counter1.increment
|
||||
counter2.increment
|
||||
counter1.increment()
|
||||
counter2.increment()
|
||||
}
|
||||
|
||||
Here's an example of using ``@Coordinated`` with a TypedActor to coordinate increments.
|
||||
|
|
@ -211,13 +216,13 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr
|
|||
import akka.transactor.Coordination._
|
||||
|
||||
trait Counter {
|
||||
@Coordinated def increment: Unit
|
||||
@Coordinated def increment()
|
||||
def get: Int
|
||||
}
|
||||
|
||||
class CounterImpl extends TypedActor with Counter {
|
||||
val ref = Ref(0)
|
||||
def increment = ref alter (_ + 1)
|
||||
def increment() { ref alter (_ + 1) }
|
||||
def get = ref.get
|
||||
}
|
||||
|
||||
|
|
@ -227,8 +232,8 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr
|
|||
val counter2 = TypedActor.newInstance(classOf[Counter], classOf[CounterImpl])
|
||||
|
||||
coordinate {
|
||||
counter1.increment
|
||||
counter2.increment
|
||||
counter1.increment()
|
||||
counter2.increment()
|
||||
}
|
||||
|
||||
TypedActor.stop(counter1)
|
||||
|
|
@ -236,9 +241,10 @@ Here's an example of using ``@Coordinated`` with a TypedActor to coordinate incr
|
|||
|
||||
The ``coordinate`` block will wait for the transactions to complete. If you do not want to wait then you can specify this explicitly:
|
||||
|
||||
`<code format="scala">`_
|
||||
coordinate(wait = false) {
|
||||
counter1.increment
|
||||
counter2.increment
|
||||
}
|
||||
`<code>`_
|
||||
.. code-block:: scala
|
||||
|
||||
coordinate(wait = false) {
|
||||
counter1.increment()
|
||||
counter2.increment()
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
Tutorial: write a scalable, fault-tolerant, persistent network chat server and client (Scala)
|
||||
Tutorial: write a scalable, fault-tolerant, network chat server and client (Scala)
|
||||
=============================================================================================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
|
|
@ -44,6 +48,8 @@ Here is a little example before we dive into a more interesting one.
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
class MyActor extends Actor {
|
||||
def receive = {
|
||||
case "test" => println("received test")
|
||||
|
|
@ -81,12 +87,53 @@ We will try to write a simple chat/IM system. It is client-server based and uses
|
|||
|
||||
We will use many of the features of Akka along the way. In particular; Actors, fault-tolerance using Actor supervision, remote Actors, Software Transactional Memory (STM) and persistence.
|
||||
|
||||
But let's start by defining the messages that will flow in our system.
|
||||
Creating an Akka SBT project
|
||||
----------------------------
|
||||
|
||||
First we need 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::
|
||||
|
||||
$ sbt
|
||||
Project does not exist, create new project? (y/N/s) y
|
||||
Name: Chat
|
||||
Organization: Hakkers Inc
|
||||
Version [1.0]:
|
||||
Scala version [2.9.0.RC1]:
|
||||
sbt version [0.7.6.RC0]:
|
||||
|
||||
Add the Akka SBT 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-M1"
|
||||
}
|
||||
|
||||
Create a project definition ``project/build/Project.scala`` file containing::
|
||||
|
||||
import sbt._
|
||||
|
||||
class ChatProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaSTM = akkaModule("stm")
|
||||
val akkaRemote = akkaModule("remote")
|
||||
}
|
||||
|
||||
|
||||
Make SBT download the dependencies it needs. That is done by invoking::
|
||||
|
||||
> reload
|
||||
> update
|
||||
|
||||
From the SBT project you can generate files for your IDE:
|
||||
|
||||
- `SbtEclipsify <https://github.com/musk/SbtEclipsify>`_ to generate Eclipse project. Detailed instructions are available in :ref:`getting-started-first-scala-eclipse`.
|
||||
- `sbt-idea <https://github.com/mpeltonen/sbt-idea>`_ to generate IntelliJ IDEA project.
|
||||
|
||||
Creating messages
|
||||
-----------------
|
||||
|
||||
It is very important that all messages that will be sent around in the system are immutable. The Actor model relies on the simple fact that no state is shared between Actors and the only way to guarantee that is to make sure we don't pass mutable state around as part of the messages.
|
||||
Let's start by defining the messages that will flow in our system. It is very important that all messages that will be sent around in the system are immutable. The Actor model relies on the simple fact that no state is shared between Actors and the only way to guarantee that is to make sure we don't pass mutable state around as part of the messages.
|
||||
|
||||
In Scala we have something called `case classes <http://www.scala-lang.org/node/107>`_. These make excellent messages since they are both immutable and great to pattern match on.
|
||||
|
||||
|
|
@ -118,7 +165,8 @@ Sometimes however, there is a need for sequential logic, sending a message and w
|
|||
def login = chat ! Login(name)
|
||||
def logout = chat ! Logout(name)
|
||||
def post(message: String) = chat ! ChatMessage(name, name + ": " + message)
|
||||
def chatLog = (chat !! GetChatLog(name)).as[ChatLog].getOrElse(throw new Exception("Couldn't get the chat log from ChatServer"))
|
||||
def chatLog = (chat !! GetChatLog(name)).as[ChatLog]
|
||||
.getOrElse(throw new Exception("Couldn't get the chat log from ChatServer"))
|
||||
}
|
||||
|
||||
As you can see, we are using the 'Actor.remote.actorFor' to lookup the chat server on the remote node. From this call we will get a handle to the remote instance and can use it as it is local.
|
||||
|
|
@ -221,7 +269,7 @@ I'll try to show you how we can make use Scala's mixins to decouple the Actor im
|
|||
protected def sessionManagement: Receive
|
||||
protected def shutdownSessions(): Unit
|
||||
|
||||
override def postStop = {
|
||||
override def postStop() = {
|
||||
EventHandler.info(this, "Chat server is shutting down...")
|
||||
shutdownSessions
|
||||
self.unlink(storage)
|
||||
|
|
@ -422,7 +470,7 @@ We have now created the full functionality for the chat server, all nicely decou
|
|||
SessionManagement with
|
||||
ChatManagement with
|
||||
MemoryChatStorageFactory {
|
||||
override def preStart = {
|
||||
override def preStart() = {
|
||||
remote.start("localhost", 2552);
|
||||
remote.register("chat:service", self) //Register the actor with the specified service id
|
||||
}
|
||||