Merge with master
This commit is contained in:
commit
009853f2f6
168 changed files with 2942 additions and 6005 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -10,6 +10,7 @@ project/boot/*
|
|||
lib_managed
|
||||
etags
|
||||
tags
|
||||
.tags
|
||||
TAGS
|
||||
akka.tmproj
|
||||
reports
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package akka.dispatch;
|
||||
|
||||
import akka.actor.Timeout;
|
||||
import akka.util.Timeout;
|
||||
import akka.actor.ActorSystem;
|
||||
|
||||
import akka.japi.*;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.scalatest.WordSpec
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
import akka.testkit._
|
||||
import akka.util.Timeout
|
||||
import akka.util.duration._
|
||||
import java.lang.IllegalStateException
|
||||
import akka.util.ReflectiveAccess
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import akka.testkit.AkkaSpec
|
|||
import akka.testkit.DefaultTimeout
|
||||
import java.util.concurrent.TimeoutException
|
||||
import akka.dispatch.Await
|
||||
import akka.util.Timeout
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class ActorTimeoutSpec extends AkkaSpec with BeforeAndAfterAll with DefaultTimeout {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package akka.actor
|
|||
|
||||
import akka.testkit._
|
||||
import akka.util.duration._
|
||||
import akka.util.Timeout
|
||||
import akka.dispatch.{ Await, Future }
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import akka.dispatch.Await
|
|||
import akka.testkit.TestEvent._
|
||||
import akka.testkit.EventFilter
|
||||
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.testkit.TestLatch
|
||||
import akka.util.duration._
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
||||
|
|
@ -29,10 +30,10 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
"ensure that slave stays dead after max restarts within time range" in {
|
||||
val boss = system.actorOf(Props[Supervisor].withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), 2, 1000)))
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val secondRestartLatch = new StandardLatch
|
||||
val restartLatch = new TestLatch
|
||||
val secondRestartLatch = new TestLatch
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val stopLatch = new StandardLatch
|
||||
val stopLatch = new TestLatch
|
||||
|
||||
val slaveProps = Props(new Actor {
|
||||
|
||||
|
|
@ -43,13 +44,13 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
|
||||
override def postRestart(reason: Throwable) = {
|
||||
if (!restartLatch.isOpen)
|
||||
restartLatch.open
|
||||
restartLatch.open()
|
||||
else
|
||||
secondRestartLatch.open
|
||||
secondRestartLatch.open()
|
||||
}
|
||||
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
stopLatch.open()
|
||||
}
|
||||
})
|
||||
val slave = Await.result((boss ? slaveProps).mapTo[ActorRef], timeout.duration)
|
||||
|
|
@ -59,17 +60,17 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Ping
|
||||
|
||||
// test restart and post restart ping
|
||||
assert(restartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(restartLatch.await(10 seconds))
|
||||
|
||||
// now crash again... should not restart
|
||||
slave ! Crash
|
||||
slave ! Ping
|
||||
|
||||
assert(secondRestartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(secondRestartLatch.await(10 seconds))
|
||||
assert(countDownLatch.await(10, TimeUnit.SECONDS))
|
||||
|
||||
slave ! Crash
|
||||
assert(stopLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(stopLatch.await(10 seconds))
|
||||
}
|
||||
|
||||
"ensure that slave is immortal without max restarts and time range" in {
|
||||
|
|
@ -97,11 +98,11 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
"ensure that slave restarts after number of crashes not within time range" in {
|
||||
val boss = system.actorOf(Props[Supervisor].withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), 2, 500)))
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val secondRestartLatch = new StandardLatch
|
||||
val thirdRestartLatch = new StandardLatch
|
||||
val pingLatch = new StandardLatch
|
||||
val secondPingLatch = new StandardLatch
|
||||
val restartLatch = new TestLatch
|
||||
val secondRestartLatch = new TestLatch
|
||||
val thirdRestartLatch = new TestLatch
|
||||
val pingLatch = new TestLatch
|
||||
val secondPingLatch = new TestLatch
|
||||
|
||||
val slaveProps = Props(new Actor {
|
||||
|
||||
|
|
@ -112,16 +113,16 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
override def postRestart(reason: Throwable) = {
|
||||
if (!restartLatch.isOpen)
|
||||
restartLatch.open
|
||||
restartLatch.open()
|
||||
else if (!secondRestartLatch.isOpen)
|
||||
secondRestartLatch.open
|
||||
secondRestartLatch.open()
|
||||
else
|
||||
thirdRestartLatch.open
|
||||
thirdRestartLatch.open()
|
||||
}
|
||||
|
||||
override def postStop() = {
|
||||
if (restartLatch.isOpen) {
|
||||
secondRestartLatch.open
|
||||
secondRestartLatch.open()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -130,14 +131,14 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Ping
|
||||
slave ! Crash
|
||||
|
||||
assert(restartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(pingLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(restartLatch.await(10 seconds))
|
||||
assert(pingLatch.await(10 seconds))
|
||||
|
||||
slave ! Ping
|
||||
slave ! Crash
|
||||
|
||||
assert(secondRestartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(secondPingLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(secondRestartLatch.await(10 seconds))
|
||||
assert(secondPingLatch.await(10 seconds))
|
||||
|
||||
// sleep to go out of the restart strategy's time range
|
||||
sleep(700L)
|
||||
|
|
@ -146,7 +147,7 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Crash
|
||||
slave ! Ping
|
||||
|
||||
assert(thirdRestartLatch.tryAwait(1, TimeUnit.SECONDS))
|
||||
assert(thirdRestartLatch.await(1 second))
|
||||
|
||||
assert(!slave.isTerminated)
|
||||
}
|
||||
|
|
@ -154,10 +155,10 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
"ensure that slave is not restarted after max retries" in {
|
||||
val boss = system.actorOf(Props[Supervisor].withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), Some(2), None)))
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val secondRestartLatch = new StandardLatch
|
||||
val restartLatch = new TestLatch
|
||||
val secondRestartLatch = new TestLatch
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val stopLatch = new StandardLatch
|
||||
val stopLatch = new TestLatch
|
||||
|
||||
val slaveProps = Props(new Actor {
|
||||
|
||||
|
|
@ -167,13 +168,13 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
override def postRestart(reason: Throwable) = {
|
||||
if (!restartLatch.isOpen)
|
||||
restartLatch.open
|
||||
restartLatch.open()
|
||||
else
|
||||
secondRestartLatch.open
|
||||
secondRestartLatch.open()
|
||||
}
|
||||
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
stopLatch.open()
|
||||
}
|
||||
})
|
||||
val slave = Await.result((boss ? slaveProps).mapTo[ActorRef], timeout.duration)
|
||||
|
|
@ -183,7 +184,7 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Ping
|
||||
|
||||
// test restart and post restart ping
|
||||
assert(restartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(restartLatch.await(10 seconds))
|
||||
|
||||
assert(!slave.isTerminated)
|
||||
|
||||
|
|
@ -191,25 +192,25 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Crash
|
||||
slave ! Ping
|
||||
|
||||
assert(secondRestartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(secondRestartLatch.await(10 seconds))
|
||||
assert(countDownLatch.await(10, TimeUnit.SECONDS))
|
||||
|
||||
sleep(700L)
|
||||
|
||||
slave ! Crash
|
||||
assert(stopLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(stopLatch.await(10 seconds))
|
||||
sleep(500L)
|
||||
assert(slave.isTerminated)
|
||||
}
|
||||
|
||||
"ensure that slave is not restarted within time range" in {
|
||||
val restartLatch, stopLatch, maxNoOfRestartsLatch = new StandardLatch
|
||||
val restartLatch, stopLatch, maxNoOfRestartsLatch = new TestLatch
|
||||
val countDownLatch = new CountDownLatch(2)
|
||||
|
||||
val boss = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case p: Props ⇒ sender ! context.watch(context.actorOf(p))
|
||||
case t: Terminated ⇒ maxNoOfRestartsLatch.open
|
||||
case t: Terminated ⇒ maxNoOfRestartsLatch.open()
|
||||
}
|
||||
}).withFaultHandler(OneForOneStrategy(List(classOf[Throwable]), None, Some(1000))))
|
||||
|
||||
|
|
@ -221,11 +222,11 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
|
||||
override def postRestart(reason: Throwable) = {
|
||||
restartLatch.open
|
||||
restartLatch.open()
|
||||
}
|
||||
|
||||
override def postStop() = {
|
||||
stopLatch.open
|
||||
stopLatch.open()
|
||||
}
|
||||
})
|
||||
val slave = Await.result((boss ? slaveProps).mapTo[ActorRef], timeout.duration)
|
||||
|
|
@ -235,7 +236,7 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
slave ! Ping
|
||||
|
||||
// test restart and post restart ping
|
||||
assert(restartLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(restartLatch.await(10 seconds))
|
||||
|
||||
assert(!slave.isTerminated)
|
||||
|
||||
|
|
@ -249,9 +250,9 @@ class RestartStrategySpec extends AkkaSpec with DefaultTimeout {
|
|||
// may not be running
|
||||
slave ! Crash
|
||||
|
||||
assert(stopLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(stopLatch.await(10 seconds))
|
||||
|
||||
assert(maxNoOfRestartsLatch.tryAwait(10, TimeUnit.SECONDS))
|
||||
assert(maxNoOfRestartsLatch.await(10 seconds))
|
||||
sleep(500L)
|
||||
assert(slave.isTerminated)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package akka.actor
|
||||
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.testkit.EventFilter
|
||||
import akka.util.duration._
|
||||
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.testkit.TestLatch
|
||||
import akka.dispatch.Await
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
@ -102,7 +102,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
object Ping
|
||||
object Crash
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val restartLatch = new TestLatch
|
||||
val pingLatch = new CountDownLatch(6)
|
||||
|
||||
val supervisor = system.actorOf(Props[Supervisor].withFaultHandler(AllForOneStrategy(List(classOf[Exception]), 3, 1000)))
|
||||
|
|
@ -122,7 +122,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
collectCancellable(system.scheduler.scheduleOnce(1000 milliseconds, actor, Crash))
|
||||
}
|
||||
|
||||
assert(restartLatch.tryAwait(2, TimeUnit.SECONDS))
|
||||
assert(restartLatch.await(2 seconds))
|
||||
// should be enough time for the ping countdown to recover and reach 6 pings
|
||||
assert(pingLatch.await(4, TimeUnit.SECONDS))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package akka.actor
|
|||
|
||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
import akka.util.Duration
|
||||
import akka.util.Timeout
|
||||
import akka.util.duration._
|
||||
import akka.serialization.Serialization
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package akka.actor.dispatch
|
|||
import org.scalatest.Assertions._
|
||||
import akka.testkit._
|
||||
import akka.dispatch._
|
||||
import akka.util.Timeout
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.{ ConcurrentHashMap, CountDownLatch, TimeUnit }
|
||||
import akka.util.Switch
|
||||
|
|
@ -204,7 +205,7 @@ object ActorModelSpec {
|
|||
await(deadline)(stats.restarts.get() == restarts)
|
||||
} catch {
|
||||
case e ⇒
|
||||
system.eventStream.publish(Error(e, dispatcher.toString, "actual: " + stats + ", required: InterceptorStats(susp=" + suspensions +
|
||||
system.eventStream.publish(Error(e, Option(dispatcher).toString, "actual: " + stats + ", required: InterceptorStats(susp=" + suspensions +
|
||||
",res=" + resumes + ",reg=" + registers + ",unreg=" + unregisters +
|
||||
",recv=" + msgsReceived + ",proc=" + msgsProcessed + ",restart=" + restarts))
|
||||
throw e
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
|||
val config = settings.config
|
||||
import config._
|
||||
|
||||
getList("akka.boot").asScala.toSeq must equal(Nil)
|
||||
getString("akka.version") must equal("2.0-SNAPSHOT")
|
||||
settings.ConfigVersion must equal("2.0-SNAPSHOT")
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import org.scalacheck.Gen._
|
|||
import akka.actor._
|
||||
import akka.testkit.{ EventFilter, filterEvents, filterException }
|
||||
import akka.util.duration._
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import akka.testkit.AkkaSpec
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import java.lang.ArithmeticException
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.testkit.TestLatch
|
||||
import java.util.concurrent.{ TimeoutException, TimeUnit, CountDownLatch }
|
||||
|
||||
object FutureSpec {
|
||||
|
|
@ -26,7 +26,7 @@ object FutureSpec {
|
|||
}
|
||||
}
|
||||
|
||||
class TestDelayActor(await: StandardLatch) extends Actor {
|
||||
class TestDelayActor(await: TestLatch) extends Actor {
|
||||
def receive = {
|
||||
case "Hello" ⇒ await.await; sender ! "World"
|
||||
case "NoReply" ⇒ await.await
|
||||
|
|
@ -69,26 +69,26 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"awaiting a result" that {
|
||||
"is not completed" must {
|
||||
behave like emptyFuture { test ⇒
|
||||
val latch = new StandardLatch
|
||||
val latch = new TestLatch
|
||||
val result = "test value"
|
||||
val future = Future {
|
||||
latch.await
|
||||
result
|
||||
}
|
||||
test(future)
|
||||
latch.open
|
||||
latch.open()
|
||||
Await.ready(future, timeout.duration)
|
||||
}
|
||||
}
|
||||
"is completed" must {
|
||||
behave like futureWithResult { test ⇒
|
||||
val latch = new StandardLatch
|
||||
val latch = new TestLatch
|
||||
val result = "test value"
|
||||
val future = Future {
|
||||
latch.await
|
||||
result
|
||||
}
|
||||
latch.open
|
||||
latch.open()
|
||||
Await.ready(future, timeout.duration)
|
||||
test(future, result)
|
||||
}
|
||||
|
|
@ -392,10 +392,10 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
}
|
||||
|
||||
"receiveShouldExecuteOnComplete" in {
|
||||
val latch = new StandardLatch
|
||||
val latch = new TestLatch
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
actor ? "Hello" onSuccess { case "World" ⇒ latch.open }
|
||||
assert(latch.tryAwait(5, TimeUnit.SECONDS))
|
||||
actor ? "Hello" onSuccess { case "World" ⇒ latch.open() }
|
||||
assert(latch.await(5 seconds))
|
||||
system.stop(actor)
|
||||
}
|
||||
|
||||
|
|
@ -425,12 +425,12 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
val f1 = Future[Any] { throw new ThrowableTest("test") }
|
||||
intercept[ThrowableTest] { Await.result(f1, timeout.duration) }
|
||||
|
||||
val latch = new StandardLatch
|
||||
val f2 = Future { latch.tryAwait(5, TimeUnit.SECONDS); "success" }
|
||||
val latch = new TestLatch
|
||||
val f2 = Future { latch.await(5 seconds); "success" }
|
||||
f2 foreach (_ ⇒ throw new ThrowableTest("dispatcher foreach"))
|
||||
f2 onSuccess { case _ ⇒ throw new ThrowableTest("dispatcher receive") }
|
||||
val f3 = f2 map (s ⇒ s.toUpperCase)
|
||||
latch.open
|
||||
latch.open()
|
||||
assert(Await.result(f2, timeout.duration) === "success")
|
||||
f2 foreach (_ ⇒ throw new ThrowableTest("current thread foreach"))
|
||||
f2 onSuccess { case _ ⇒ throw new ThrowableTest("current thread receive") }
|
||||
|
|
@ -439,13 +439,13 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
}
|
||||
|
||||
"shouldBlockUntilResult" in {
|
||||
val latch = new StandardLatch
|
||||
val latch = new TestLatch
|
||||
|
||||
val f = Future { latch.await; 5 }
|
||||
val f2 = Future { Await.result(f, timeout.duration) + 5 }
|
||||
|
||||
intercept[TimeoutException](Await.ready(f2, 100 millis))
|
||||
latch.open
|
||||
latch.open()
|
||||
assert(Await.result(f2, timeout.duration) === 10)
|
||||
|
||||
val f3 = Future { Thread.sleep(100); 5 }
|
||||
|
|
@ -514,19 +514,19 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
import Future.flow
|
||||
|
||||
val x, y, z = Promise[Int]()
|
||||
val ly, lz = new StandardLatch
|
||||
val ly, lz = new TestLatch
|
||||
|
||||
val result = flow {
|
||||
y completeWith x
|
||||
ly.open // not within continuation
|
||||
ly.open() // not within continuation
|
||||
|
||||
z << x
|
||||
lz.open // within continuation, will wait for 'z' to complete
|
||||
lz.open() // within continuation, will wait for 'z' to complete
|
||||
z() + y()
|
||||
}
|
||||
|
||||
assert(ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
assert(ly.await(100 milliseconds))
|
||||
lz.awaitTimeout(100 milliseconds)
|
||||
|
||||
flow { x << 5 }
|
||||
|
||||
|
|
@ -579,7 +579,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"futureDataFlowShouldEmulateBlocking2" in {
|
||||
import Future.flow
|
||||
val x1, x2, y1, y2 = Promise[Int]()
|
||||
val lx, ly, lz = new StandardLatch
|
||||
val lx, ly, lz = new TestLatch
|
||||
val result = flow {
|
||||
lx.open()
|
||||
x1 << y1
|
||||
|
|
@ -588,20 +588,20 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
lz.open()
|
||||
x1() + x2()
|
||||
}
|
||||
assert(lx.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(lx.await(2 seconds))
|
||||
assert(!ly.isOpen)
|
||||
assert(!lz.isOpen)
|
||||
assert(List(x1, x2, y1, y2).forall(_.isCompleted == false))
|
||||
|
||||
flow { y1 << 1 } // When this is set, it should cascade down the line
|
||||
|
||||
assert(ly.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(ly.await(2 seconds))
|
||||
assert(Await.result(x1, 1 minute) === 1)
|
||||
assert(!lz.isOpen)
|
||||
|
||||
flow { y2 << 9 } // When this is set, it should cascade down the line
|
||||
|
||||
assert(lz.tryAwaitUninterruptible(2000, TimeUnit.MILLISECONDS))
|
||||
assert(lz.await(2 seconds))
|
||||
assert(Await.result(x2, 1 minute) === 9)
|
||||
|
||||
assert(List(x1, x2, y1, y2).forall(_.isCompleted))
|
||||
|
|
@ -612,20 +612,20 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"dataFlowAPIshouldbeSlick" in {
|
||||
import Future.flow
|
||||
|
||||
val i1, i2, s1, s2 = new StandardLatch
|
||||
val i1, i2, s1, s2 = new TestLatch
|
||||
|
||||
val callService1 = Future { i1.open; s1.awaitUninterruptible; 1 }
|
||||
val callService2 = Future { i2.open; s2.awaitUninterruptible; 9 }
|
||||
val callService1 = Future { i1.open(); s1.await; 1 }
|
||||
val callService2 = Future { i2.open(); s2.await; 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(i1.await(2 seconds))
|
||||
assert(i2.await(2 seconds))
|
||||
s1.open()
|
||||
s2.open()
|
||||
assert(Await.result(result, timeout.duration) === 10)
|
||||
}
|
||||
|
||||
|
|
@ -634,19 +634,19 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
import Future.flow
|
||||
|
||||
val x, y, z = Promise[Int]()
|
||||
val ly, lz = new StandardLatch
|
||||
val ly, lz = new TestLatch
|
||||
|
||||
val result = flow {
|
||||
y << x
|
||||
ly.open
|
||||
ly.open()
|
||||
val oops = 1 / 0
|
||||
z << x
|
||||
lz.open
|
||||
lz.open()
|
||||
z() + y() + oops
|
||||
}
|
||||
|
||||
assert(!ly.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
assert(!lz.tryAwaitUninterruptible(100, TimeUnit.MILLISECONDS))
|
||||
ly.awaitTimeout(100 milliseconds)
|
||||
lz.awaitTimeout(100 milliseconds)
|
||||
|
||||
flow { x << 5 }
|
||||
|
||||
|
|
@ -660,7 +660,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"futureContinuationsShouldNotBlock" in {
|
||||
import Future.flow
|
||||
|
||||
val latch = new StandardLatch
|
||||
val latch = new TestLatch
|
||||
val future = Future {
|
||||
latch.await
|
||||
"Hello"
|
||||
|
|
@ -672,7 +672,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
assert(!result.isCompleted)
|
||||
|
||||
latch.open
|
||||
latch.open()
|
||||
|
||||
assert(Await.result(result, timeout.duration) === Some("Hello"))
|
||||
}
|
||||
|
|
@ -743,39 +743,39 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
}
|
||||
|
||||
"run callbacks async" in {
|
||||
val latch = Vector.fill(10)(new StandardLatch)
|
||||
val latch = Vector.fill(10)(new TestLatch)
|
||||
|
||||
val f1 = Future { latch(0).open; latch(1).await; "Hello" }
|
||||
val f2 = f1 map { s ⇒ latch(2).open; latch(3).await; s.length }
|
||||
f2 foreach (_ ⇒ latch(4).open)
|
||||
val f1 = Future { latch(0).open(); latch(1).await; "Hello" }
|
||||
val f2 = f1 map { s ⇒ latch(2).open(); latch(3).await; s.length }
|
||||
f2 foreach (_ ⇒ latch(4).open())
|
||||
|
||||
latch(0).await
|
||||
|
||||
f1 must not be ('completed)
|
||||
f2 must not be ('completed)
|
||||
|
||||
latch(1).open
|
||||
latch(1).open()
|
||||
latch(2).await
|
||||
|
||||
f1 must be('completed)
|
||||
f2 must not be ('completed)
|
||||
|
||||
val f3 = f1 map { s ⇒ latch(5).open; latch(6).await; s.length * 2 }
|
||||
f3 foreach (_ ⇒ latch(3).open)
|
||||
val f3 = f1 map { s ⇒ latch(5).open(); latch(6).await; s.length * 2 }
|
||||
f3 foreach (_ ⇒ latch(3).open())
|
||||
|
||||
latch(5).await
|
||||
|
||||
f3 must not be ('completed)
|
||||
|
||||
latch(6).open
|
||||
latch(6).open()
|
||||
latch(4).await
|
||||
|
||||
f2 must be('completed)
|
||||
f3 must be('completed)
|
||||
|
||||
val p1 = Promise[String]()
|
||||
val f4 = p1 map { s ⇒ latch(7).open; latch(8).await; s.length }
|
||||
f4 foreach (_ ⇒ latch(9).open)
|
||||
val f4 = p1 map { s ⇒ latch(7).open(); latch(8).await; s.length }
|
||||
f4 foreach (_ ⇒ latch(9).open())
|
||||
|
||||
p1 must not be ('completed)
|
||||
f4 must not be ('completed)
|
||||
|
|
@ -787,7 +787,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
p1 must be('completed)
|
||||
f4 must not be ('completed)
|
||||
|
||||
latch(8).open
|
||||
latch(8).open()
|
||||
latch(9).await
|
||||
|
||||
Await.ready(f4, timeout.duration) must be('completed)
|
||||
|
|
@ -797,13 +797,13 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
val simple = Future() map (_ ⇒ Await.result((Future(()) map (_ ⇒ ())), timeout.duration))
|
||||
Await.ready(simple, timeout.duration) must be('completed)
|
||||
|
||||
val l1, l2 = new StandardLatch
|
||||
val l1, l2 = new TestLatch
|
||||
val complex = Future() map { _ ⇒
|
||||
Future.blocking(system.dispatcher)
|
||||
val nested = Future(())
|
||||
nested foreach (_ ⇒ l1.open)
|
||||
nested foreach (_ ⇒ l1.open())
|
||||
l1.await // make sure nested is completed
|
||||
nested foreach (_ ⇒ l2.open)
|
||||
nested foreach (_ ⇒ l2.open())
|
||||
l2.await
|
||||
}
|
||||
Await.ready(complex, timeout.duration) must be('completed)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package akka.dispatch
|
|||
|
||||
import Future.flow
|
||||
import akka.util.cps._
|
||||
import akka.actor.Timeout
|
||||
import akka.util.Timeout
|
||||
import akka.util.duration._
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.testkit.DefaultTimeout
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
|
||||
"no router" must {
|
||||
"be started when constructed" in {
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(NoRouter))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(NoRouter))
|
||||
routedActor.isTerminated must be(false)
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
|
||||
"round robin router" must {
|
||||
"be started when constructed" in {
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RoundRobinRouter(nrOfInstances = 1)))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(nrOfInstances = 1)))
|
||||
routedActor.isTerminated must be(false)
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
actors = actors :+ actor
|
||||
}
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RoundRobinRouter(targets = actors)))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(targets = actors)))
|
||||
|
||||
//send messages to the actor.
|
||||
for (i ← 0 until iterationCount) {
|
||||
|
|
@ -157,7 +157,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
}
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RoundRobinRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(targets = List(actor1, actor2))))
|
||||
|
||||
routedActor ! Broadcast(1)
|
||||
routedActor ! Broadcast("end")
|
||||
|
|
@ -172,7 +172,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
"random router" must {
|
||||
|
||||
"be started when constructed" in {
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RandomRouter(nrOfInstances = 1)))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(RandomRouter(nrOfInstances = 1)))
|
||||
routedActor.isTerminated must be(false)
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
}
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RandomRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(RandomRouter(targets = List(actor1, actor2))))
|
||||
|
||||
routedActor ! Broadcast(1)
|
||||
routedActor ! Broadcast("end")
|
||||
|
|
@ -209,7 +209,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
|
||||
"broadcast router" must {
|
||||
"be started when constructed" in {
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(BroadcastRouter(nrOfInstances = 1)))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(BroadcastRouter(nrOfInstances = 1)))
|
||||
routedActor.isTerminated must be(false)
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
}
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
routedActor ! 1
|
||||
routedActor ! "end"
|
||||
|
||||
|
|
@ -263,7 +263,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
}
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
routedActor ? 1
|
||||
routedActor ! "end"
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
"Scatter-gather router" must {
|
||||
|
||||
"be started when constructed" in {
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(ScatterGatherFirstCompletedRouter(targets = List(newActor(0)))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(ScatterGatherFirstCompletedRouter(targets = List(newActor(0)))))
|
||||
routedActor.isTerminated must be(false)
|
||||
}
|
||||
|
||||
|
|
@ -300,7 +300,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
}
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(ScatterGatherFirstCompletedRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(ScatterGatherFirstCompletedRouter(targets = List(actor1, actor2))))
|
||||
routedActor ! Broadcast(1)
|
||||
routedActor ! Broadcast("end")
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val shutdownLatch = new TestLatch(1)
|
||||
val actor1 = newActor(1, Some(shutdownLatch))
|
||||
val actor2 = newActor(22, Some(shutdownLatch))
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(ScatterGatherFirstCompletedRouter(targets = List(actor1, actor2))))
|
||||
val routedActor = system.actorOf(Props[TestActor].withRouter(ScatterGatherFirstCompletedRouter(targets = List(actor1, actor2))))
|
||||
|
||||
routedActor ! Broadcast(Stop(Some(1)))
|
||||
shutdownLatch.await
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ class DurationSpec extends WordSpec with MustMatchers {
|
|||
(inf - minf) must be(inf)
|
||||
(minf - inf) must be(minf)
|
||||
(minf + minf) must be(minf)
|
||||
assert(inf == inf)
|
||||
assert(minf == minf)
|
||||
inf.compareTo(inf) must be(0)
|
||||
inf.compareTo(one) must be(1)
|
||||
minf.compareTo(minf) must be(0)
|
||||
minf.compareTo(one) must be(-1)
|
||||
assert(inf != minf)
|
||||
assert(minf != inf)
|
||||
assert(one != inf)
|
||||
assert(minf != one)
|
||||
}
|
||||
|
||||
"support fromNow" in {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.Set;
|
|||
* is a key in a JSON object; it's just a string that's the key in a map. A
|
||||
* "path" is a parseable expression with a syntax and it refers to a series of
|
||||
* keys. Path expressions are described in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">spec for
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec for
|
||||
* Human-Optimized Config Object Notation</a>. In brief, a path is
|
||||
* period-separated so "a.b.c" looks for key c in object b in object a in the
|
||||
* root object. Sometimes double quotes are needed around special characters in
|
||||
|
|
@ -97,7 +97,7 @@ public interface Config extends ConfigMergeable {
|
|||
/**
|
||||
* Returns a replacement config with all substitutions (the
|
||||
* <code>${foo.bar}</code> syntax, see <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
||||
* spec</a>) resolved. Substitutions are looked up using this
|
||||
* <code>Config</code> as the root object, that is, a substitution
|
||||
* <code>${foo.bar}</code> will be replaced with the result of
|
||||
|
|
@ -395,7 +395,8 @@ public interface Config extends ConfigMergeable {
|
|||
* Gets a value as a size in bytes (parses special strings like "128M"). If
|
||||
* the value is already a number, then it's left alone; if it's a string,
|
||||
* it's parsed understanding unit suffixes such as "128K", as documented in
|
||||
* the <a href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||
* the <a
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
||||
* spec</a>.
|
||||
*
|
||||
* @param path
|
||||
|
|
@ -414,7 +415,7 @@ public interface Config extends ConfigMergeable {
|
|||
* Get value as a duration in milliseconds. If the value is already a
|
||||
* number, then it's left alone; if it's a string, it's parsed understanding
|
||||
* units suffixes like "10m" or "5ns" as documented in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">the
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
||||
* spec</a>.
|
||||
*
|
||||
* @param path
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ package com.typesafe.config;
|
|||
|
||||
|
||||
/**
|
||||
* All exceptions thrown by the library are subclasses of ConfigException.
|
||||
* All exceptions thrown by the library are subclasses of
|
||||
* <code>ConfigException</code>.
|
||||
*/
|
||||
public abstract class ConfigException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
@ -338,6 +339,9 @@ public abstract class ConfigException extends RuntimeException {
|
|||
sb.append(p.problem());
|
||||
sb.append(", ");
|
||||
}
|
||||
if (sb.length() == 0)
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"ValidationFailed must have a non-empty list of problems");
|
||||
sb.setLength(sb.length() - 2); // chop comma and space
|
||||
|
||||
return sb.toString();
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ public final class ConfigFactory {
|
|||
/**
|
||||
* Converts a Java {@link java.util.Properties} object to a
|
||||
* {@link ConfigObject} using the rules documented in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON
|
||||
* spec</a>. The keys in the <code>Properties</code> object are split on the
|
||||
* period character '.' and treated as paths. The values will all end up as
|
||||
* string values. If you have both "a=foo" and "a.b=bar" in your properties
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ public interface ConfigMergeable {
|
|||
*
|
||||
* <p>
|
||||
* The semantics of merging are described in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">spec for
|
||||
* HOCON</a>.
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec
|
||||
* for HOCON</a>.
|
||||
*
|
||||
* <p>
|
||||
* Note that objects do not merge "across" non-objects; if you write
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import java.util.Map;
|
|||
* The API for a {@code ConfigObject} is in terms of keys, while the API for a
|
||||
* {@link Config} is in terms of path expressions. Conceptually,
|
||||
* {@code ConfigObject} is a tree of maps from keys to values, while a
|
||||
* {@code ConfigObject} is a one-level map from paths to values.
|
||||
* {@code Config} is a one-level map from paths to values.
|
||||
*
|
||||
* <p>
|
||||
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package com.typesafe.config;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -66,4 +67,16 @@ public interface ConfigOrigin {
|
|||
* @return line number or -1 if none is available
|
||||
*/
|
||||
public int lineNumber();
|
||||
|
||||
/**
|
||||
* Returns any comments that appeared to "go with" this place in the file.
|
||||
* Often an empty list, but never null. The details of this are subject to
|
||||
* change, but at the moment comments that are immediately before an array
|
||||
* element or object field, with no blank line after the comment, "go with"
|
||||
* that element or field.
|
||||
*
|
||||
* @return any comments that seemed to "go with" this origin, empty list if
|
||||
* none
|
||||
*/
|
||||
public List<String> comments();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ package com.typesafe.config;
|
|||
/**
|
||||
* A set of options related to resolving substitutions. Substitutions use the
|
||||
* <code>${foo.bar}</code> syntax and are documented in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a> spec.
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
||||
* spec.
|
||||
* <p>
|
||||
* This object is immutable, so the "setters" return a new object.
|
||||
* <p>
|
||||
* Here is an example of creating a custom {@code ConfigResolveOptions}:
|
||||
*
|
||||
* <pre>
|
||||
* ConfigResolveOptions options = ConfigResolveOptions.defaults()
|
||||
* .setUseSystemEnvironment(false)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ package com.typesafe.config;
|
|||
|
||||
/**
|
||||
* The syntax of a character stream, <a href="http://json.org">JSON</a>, <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a> aka
|
||||
* ".conf", or <a href=
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
|
||||
* aka ".conf", or <a href=
|
||||
* "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
|
||||
* >Java properties</a>.
|
||||
*
|
||||
|
|
@ -19,8 +19,8 @@ public enum ConfigSyntax {
|
|||
JSON,
|
||||
/**
|
||||
* The JSON-superset <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON</a>
|
||||
* format.
|
||||
* href="https://github.com/typesafehub/config/blob/master/HOCON.md"
|
||||
* >HOCON</a> format.
|
||||
*/
|
||||
CONF,
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import java.util.List;
|
|||
|
||||
import com.typesafe.config.impl.ConfigImplUtil;
|
||||
|
||||
/**
|
||||
* Contains static utility methods.
|
||||
*
|
||||
*/
|
||||
public final class ConfigUtil {
|
||||
private ConfigUtil() {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import java.util.Map;
|
|||
import com.typesafe.config.impl.ConfigImpl;
|
||||
|
||||
/**
|
||||
* This class holds some static factory methods for building ConfigValue. See
|
||||
* also ConfigFactory which has methods for parsing files and certain in-memory
|
||||
* data structures.
|
||||
* This class holds some static factory methods for building {@link ConfigValue}
|
||||
* instances. See also {@link ConfigFactory} which has methods for parsing files
|
||||
* and certain in-memory data structures.
|
||||
*/
|
||||
public final class ConfigValueFactory {
|
||||
private ConfigValueFactory() {
|
||||
|
|
|
|||
|
|
@ -111,12 +111,12 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||
return ConfigValueType.OBJECT;
|
||||
}
|
||||
|
||||
protected abstract AbstractConfigObject newCopy(ResolveStatus status,
|
||||
boolean ignoresFallbacks);
|
||||
protected abstract AbstractConfigObject newCopy(ResolveStatus status, boolean ignoresFallbacks,
|
||||
ConfigOrigin origin);
|
||||
|
||||
@Override
|
||||
protected AbstractConfigObject newCopy(boolean ignoresFallbacks) {
|
||||
return newCopy(resolveStatus(), ignoresFallbacks);
|
||||
protected AbstractConfigObject newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return newCopy(resolveStatus(), ignoresFallbacks, origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -173,7 +173,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||
return new SimpleConfigObject(mergeOrigins(this, fallback), merged, newResolveStatus,
|
||||
newIgnoresFallbacks);
|
||||
else if (newResolveStatus != resolveStatus() || newIgnoresFallbacks != ignoresFallbacks())
|
||||
return newCopy(newResolveStatus, newIgnoresFallbacks);
|
||||
return newCopy(newResolveStatus, newIgnoresFallbacks, origin());
|
||||
else
|
||||
return this;
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||
}
|
||||
}
|
||||
if (changes == null) {
|
||||
return newCopy(newResolveStatus, ignoresFallbacks());
|
||||
return newCopy(newResolveStatus, ignoresFallbacks(), origin());
|
||||
} else {
|
||||
Map<String, AbstractConfigValue> modified = new HashMap<String, AbstractConfigValue>();
|
||||
for (String k : keySet()) {
|
||||
|
|
@ -306,6 +306,12 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||
sb.append("# ");
|
||||
sb.append(v.origin().description());
|
||||
sb.append("\n");
|
||||
for (String comment : v.origin().comments()) {
|
||||
indent(sb, indent + 1);
|
||||
sb.append("# ");
|
||||
sb.append(comment);
|
||||
sb.append("\n");
|
||||
}
|
||||
indent(sb, indent + 1);
|
||||
}
|
||||
v.render(sb, indent + 1, k, formatted);
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ import com.typesafe.config.ConfigValue;
|
|||
*/
|
||||
abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
||||
|
||||
final private ConfigOrigin origin;
|
||||
final private SimpleConfigOrigin origin;
|
||||
|
||||
AbstractConfigValue(ConfigOrigin origin) {
|
||||
this.origin = origin;
|
||||
this.origin = (SimpleConfigOrigin) origin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOrigin origin() {
|
||||
public SimpleConfigOrigin origin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
|
|
@ -76,9 +76,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
return this;
|
||||
}
|
||||
|
||||
protected AbstractConfigValue newCopy(boolean ignoresFallbacks) {
|
||||
return this;
|
||||
}
|
||||
protected abstract AbstractConfigValue newCopy(boolean ignoresFallbacks, ConfigOrigin origin);
|
||||
|
||||
// this is virtualized rather than a field because only some subclasses
|
||||
// really need to store the boolean, and they may be able to pack it
|
||||
|
|
@ -105,6 +103,13 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
throw badMergeException();
|
||||
}
|
||||
|
||||
public AbstractConfigValue withOrigin(ConfigOrigin origin) {
|
||||
if (this.origin == origin)
|
||||
return this;
|
||||
else
|
||||
return newCopy(ignoresFallbacks(), origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
|
||||
if (ignoresFallbacks()) {
|
||||
|
|
@ -118,7 +123,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
AbstractConfigObject fallback = (AbstractConfigObject) other;
|
||||
if (fallback.resolveStatus() == ResolveStatus.RESOLVED && fallback.isEmpty()) {
|
||||
if (fallback.ignoresFallbacks())
|
||||
return newCopy(true /* ignoresFallbacks */);
|
||||
return newCopy(true /* ignoresFallbacks */, origin);
|
||||
else
|
||||
return this;
|
||||
} else {
|
||||
|
|
@ -128,7 +133,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
// falling back to a non-object doesn't merge anything, and also
|
||||
// prohibits merging any objects that we fall back to later.
|
||||
// so we have to switch to ignoresFallbacks mode.
|
||||
return newCopy(true /* ignoresFallbacks */);
|
||||
return newCopy(true /* ignoresFallbacks */, origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,4 +29,9 @@ final class ConfigBoolean extends AbstractConfigValue {
|
|||
String transformToString() {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigBoolean newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigBoolean(origin, value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
|||
return ignoresFallbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue newCopy(boolean newIgnoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return new ConfigDelayedMerge(newOrigin, stack, newIgnoresFallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ConfigDelayedMerge mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||
if (ignoresFallbacks)
|
||||
|
|
@ -196,6 +201,12 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
|||
i += 1;
|
||||
sb.append(v.origin().description());
|
||||
sb.append("\n");
|
||||
for (String comment : v.origin().comments()) {
|
||||
indent(sb, indent);
|
||||
sb.append("# ");
|
||||
sb.append(comment);
|
||||
sb.append("\n");
|
||||
}
|
||||
indent(sb, indent);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ConfigDelayedMergeObject newCopy(ResolveStatus status,
|
||||
boolean ignoresFallbacks) {
|
||||
protected ConfigDelayedMergeObject newCopy(ResolveStatus status, boolean ignoresFallbacks,
|
||||
ConfigOrigin origin) {
|
||||
if (status != resolveStatus())
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"attempt to create resolved ConfigDelayedMergeObject");
|
||||
return new ConfigDelayedMergeObject(origin(), stack, ignoresFallbacks);
|
||||
return new ConfigDelayedMergeObject(origin, stack, ignoresFallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -43,4 +43,9 @@ final class ConfigDouble extends ConfigNumber {
|
|||
protected double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigDouble newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigDouble(origin, value, originalText);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,4 +43,9 @@ final class ConfigInt extends ConfigNumber {
|
|||
protected double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigInt newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigInt(origin, value, originalText);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,4 +43,9 @@ final class ConfigLong extends ConfigNumber {
|
|||
protected double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigLong newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigLong(origin, value, originalText);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,9 @@ final class ConfigNull extends AbstractConfigValue {
|
|||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
sb.append("null");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigNull newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigNull(origin);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ abstract class ConfigNumber extends AbstractConfigValue {
|
|||
// a sentence) we always have it exactly as the person typed it into the
|
||||
// config file. It's purely cosmetic; equals/hashCode don't consider this
|
||||
// for example.
|
||||
final private String originalText;
|
||||
final protected String originalText;
|
||||
|
||||
protected ConfigNumber(ConfigOrigin origin, String originalText) {
|
||||
super(origin);
|
||||
|
|
|
|||
|
|
@ -34,4 +34,9 @@ final class ConfigString extends AbstractConfigValue {
|
|||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
sb.append(ConfigImplUtil.renderJsonString(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigString newCopy(boolean ignoresFallbacks, ConfigOrigin origin) {
|
||||
return new ConfigString(origin, value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ConfigSubstitution newCopy(boolean ignoresFallbacks) {
|
||||
return new ConfigSubstitution(origin(), pieces, prefixLength, ignoresFallbacks);
|
||||
protected ConfigSubstitution newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return new ConfigSubstitution(newOrigin, pieces, prefixLength, ignoresFallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -32,9 +32,53 @@ final class Parser {
|
|||
return context.parse();
|
||||
}
|
||||
|
||||
static private final class TokenWithComments {
|
||||
final Token token;
|
||||
final List<Token> comments;
|
||||
|
||||
TokenWithComments(Token token, List<Token> comments) {
|
||||
this.token = token;
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
TokenWithComments(Token token) {
|
||||
this(token, Collections.<Token> emptyList());
|
||||
}
|
||||
|
||||
TokenWithComments prepend(List<Token> earlier) {
|
||||
if (this.comments.isEmpty()) {
|
||||
return new TokenWithComments(token, earlier);
|
||||
} else {
|
||||
List<Token> merged = new ArrayList<Token>();
|
||||
merged.addAll(earlier);
|
||||
merged.addAll(comments);
|
||||
return new TokenWithComments(token, merged);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleConfigOrigin setComments(SimpleConfigOrigin origin) {
|
||||
if (comments.isEmpty()) {
|
||||
return origin;
|
||||
} else {
|
||||
List<String> newComments = new ArrayList<String>();
|
||||
for (Token c : comments) {
|
||||
newComments.add(Tokens.getCommentText(c));
|
||||
}
|
||||
return origin.setComments(newComments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// this ends up in user-visible error messages, so we don't want the
|
||||
// comments
|
||||
return token.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static private final class ParseContext {
|
||||
private int lineNumber;
|
||||
final private Stack<Token> buffer;
|
||||
final private Stack<TokenWithComments> buffer;
|
||||
final private Iterator<Token> tokens;
|
||||
final private ConfigIncluder includer;
|
||||
final private ConfigIncludeContext includeContext;
|
||||
|
|
@ -50,7 +94,7 @@ final class Parser {
|
|||
Iterator<Token> tokens, ConfigIncluder includer,
|
||||
ConfigIncludeContext includeContext) {
|
||||
lineNumber = 1;
|
||||
buffer = new Stack<Token>();
|
||||
buffer = new Stack<TokenWithComments>();
|
||||
this.tokens = tokens;
|
||||
this.flavor = flavor;
|
||||
this.baseOrigin = origin;
|
||||
|
|
@ -60,13 +104,66 @@ final class Parser {
|
|||
this.equalsCount = 0;
|
||||
}
|
||||
|
||||
private Token nextToken() {
|
||||
Token t = null;
|
||||
if (buffer.isEmpty()) {
|
||||
t = tokens.next();
|
||||
} else {
|
||||
t = buffer.pop();
|
||||
private void consolidateCommentBlock(Token commentToken) {
|
||||
// a comment block "goes with" the following token
|
||||
// unless it's separated from it by a blank line.
|
||||
// we want to build a list of newline tokens followed
|
||||
// by a non-newline non-comment token; with all comments
|
||||
// associated with that final non-newline non-comment token.
|
||||
List<Token> newlines = new ArrayList<Token>();
|
||||
List<Token> comments = new ArrayList<Token>();
|
||||
|
||||
Token previous = null;
|
||||
Token next = commentToken;
|
||||
while (true) {
|
||||
if (Tokens.isNewline(next)) {
|
||||
if (previous != null && Tokens.isNewline(previous)) {
|
||||
// blank line; drop all comments to this point and
|
||||
// start a new comment block
|
||||
comments.clear();
|
||||
}
|
||||
newlines.add(next);
|
||||
} else if (Tokens.isComment(next)) {
|
||||
comments.add(next);
|
||||
} else {
|
||||
// a non-newline non-comment token
|
||||
break;
|
||||
}
|
||||
|
||||
previous = next;
|
||||
next = tokens.next();
|
||||
}
|
||||
|
||||
// put our concluding token in the queue with all the comments
|
||||
// attached
|
||||
buffer.push(new TokenWithComments(next, comments));
|
||||
|
||||
// now put all the newlines back in front of it
|
||||
ListIterator<Token> li = newlines.listIterator(newlines.size());
|
||||
while (li.hasPrevious()) {
|
||||
buffer.push(new TokenWithComments(li.previous()));
|
||||
}
|
||||
}
|
||||
|
||||
private TokenWithComments popToken() {
|
||||
if (buffer.isEmpty()) {
|
||||
Token t = tokens.next();
|
||||
if (Tokens.isComment(t)) {
|
||||
consolidateCommentBlock(t);
|
||||
return buffer.pop();
|
||||
} else {
|
||||
return new TokenWithComments(t);
|
||||
}
|
||||
} else {
|
||||
return buffer.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private TokenWithComments nextToken() {
|
||||
TokenWithComments withComments = null;
|
||||
|
||||
withComments = popToken();
|
||||
Token t = withComments.token;
|
||||
|
||||
if (Tokens.isProblem(t)) {
|
||||
ConfigOrigin origin = t.origin();
|
||||
|
|
@ -79,8 +176,7 @@ final class Parser {
|
|||
message = addKeyName(message);
|
||||
}
|
||||
throw new ConfigException.Parse(origin, message, cause);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
if (Tokens.isUnquotedText(t)) {
|
||||
throw parseError(addKeyName("Token not allowed in valid JSON: '"
|
||||
|
|
@ -90,21 +186,25 @@ final class Parser {
|
|||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
return withComments;
|
||||
}
|
||||
}
|
||||
|
||||
private void putBack(Token token) {
|
||||
private void putBack(TokenWithComments token) {
|
||||
buffer.push(token);
|
||||
}
|
||||
|
||||
private Token nextTokenIgnoringNewline() {
|
||||
Token t = nextToken();
|
||||
while (Tokens.isNewline(t)) {
|
||||
private TokenWithComments nextTokenIgnoringNewline() {
|
||||
TokenWithComments t = nextToken();
|
||||
|
||||
while (Tokens.isNewline(t.token)) {
|
||||
// line number tokens have the line that was _ended_ by the
|
||||
// newline, so we have to add one.
|
||||
lineNumber = t.lineNumber() + 1;
|
||||
lineNumber = t.token.lineNumber() + 1;
|
||||
|
||||
t = nextToken();
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
@ -116,8 +216,8 @@ final class Parser {
|
|||
// is left just after the comma or the newline.
|
||||
private boolean checkElementSeparator() {
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.COMMA) {
|
||||
TokenWithComments t = nextTokenIgnoringNewline();
|
||||
if (t.token == Tokens.COMMA) {
|
||||
return true;
|
||||
} else {
|
||||
putBack(t);
|
||||
|
|
@ -125,15 +225,16 @@ final class Parser {
|
|||
}
|
||||
} else {
|
||||
boolean sawSeparatorOrNewline = false;
|
||||
Token t = nextToken();
|
||||
TokenWithComments t = nextToken();
|
||||
while (true) {
|
||||
if (Tokens.isNewline(t)) {
|
||||
if (Tokens.isNewline(t.token)) {
|
||||
// newline number is the line just ended, so add one
|
||||
lineNumber = t.lineNumber() + 1;
|
||||
lineNumber = t.token.lineNumber() + 1;
|
||||
sawSeparatorOrNewline = true;
|
||||
|
||||
// we want to continue to also eat
|
||||
// a comma if there is one.
|
||||
} else if (t == Tokens.COMMA) {
|
||||
} else if (t.token == Tokens.COMMA) {
|
||||
return true;
|
||||
} else {
|
||||
// non-newline-or-comma
|
||||
|
|
@ -154,12 +255,17 @@ final class Parser {
|
|||
return;
|
||||
|
||||
List<Token> values = null; // create only if we have value tokens
|
||||
Token t = nextTokenIgnoringNewline(); // ignore a newline up front
|
||||
while (Tokens.isValue(t) || Tokens.isUnquotedText(t)
|
||||
|| Tokens.isSubstitution(t)) {
|
||||
if (values == null)
|
||||
TokenWithComments firstValueWithComments = null;
|
||||
TokenWithComments t = nextTokenIgnoringNewline(); // ignore a
|
||||
// newline up
|
||||
// front
|
||||
while (Tokens.isValue(t.token) || Tokens.isUnquotedText(t.token)
|
||||
|| Tokens.isSubstitution(t.token)) {
|
||||
if (values == null) {
|
||||
values = new ArrayList<Token>();
|
||||
values.add(t);
|
||||
firstValueWithComments = t;
|
||||
}
|
||||
values.add(t.token);
|
||||
t = nextToken(); // but don't consolidate across a newline
|
||||
}
|
||||
// the last one wasn't a value token
|
||||
|
|
@ -168,9 +274,9 @@ final class Parser {
|
|||
if (values == null)
|
||||
return;
|
||||
|
||||
if (values.size() == 1 && Tokens.isValue(values.get(0))) {
|
||||
if (values.size() == 1 && Tokens.isValue(firstValueWithComments.token)) {
|
||||
// a single value token requires no consolidation
|
||||
putBack(values.get(0));
|
||||
putBack(firstValueWithComments);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +341,7 @@ final class Parser {
|
|||
firstOrigin, minimized));
|
||||
}
|
||||
|
||||
putBack(consolidated);
|
||||
putBack(new TokenWithComments(consolidated, firstValueWithComments.comments));
|
||||
}
|
||||
|
||||
private ConfigOrigin lineOrigin() {
|
||||
|
|
@ -309,17 +415,23 @@ final class Parser {
|
|||
return part + ")";
|
||||
}
|
||||
|
||||
private AbstractConfigValue parseValue(Token token) {
|
||||
if (Tokens.isValue(token)) {
|
||||
return Tokens.getValue(token);
|
||||
} else if (token == Tokens.OPEN_CURLY) {
|
||||
return parseObject(true);
|
||||
} else if (token == Tokens.OPEN_SQUARE) {
|
||||
return parseArray();
|
||||
private AbstractConfigValue parseValue(TokenWithComments t) {
|
||||
AbstractConfigValue v;
|
||||
|
||||
if (Tokens.isValue(t.token)) {
|
||||
v = Tokens.getValue(t.token);
|
||||
} else if (t.token == Tokens.OPEN_CURLY) {
|
||||
v = parseObject(true);
|
||||
} else if (t.token == Tokens.OPEN_SQUARE) {
|
||||
v = parseArray();
|
||||
} else {
|
||||
throw parseError(addQuoteSuggestion(token.toString(),
|
||||
"Expecting a value but got wrong token: " + token));
|
||||
throw parseError(addQuoteSuggestion(t.token.toString(),
|
||||
"Expecting a value but got wrong token: " + t.token));
|
||||
}
|
||||
|
||||
v = v.withOrigin(t.setComments(v.origin()));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private static AbstractConfigObject createValueUnderPath(Path path,
|
||||
|
|
@ -339,24 +451,29 @@ final class Parser {
|
|||
remaining = remaining.remainder();
|
||||
}
|
||||
}
|
||||
|
||||
// the setComments(null) is to ensure comments are only
|
||||
// on the exact leaf node they apply to.
|
||||
// a comment before "foo.bar" applies to the full setting
|
||||
// "foo.bar" not also to "foo"
|
||||
ListIterator<String> i = keys.listIterator(keys.size());
|
||||
String deepest = i.previous();
|
||||
AbstractConfigObject o = new SimpleConfigObject(value.origin(),
|
||||
AbstractConfigObject o = new SimpleConfigObject(value.origin().setComments(null),
|
||||
Collections.<String, AbstractConfigValue> singletonMap(
|
||||
deepest, value));
|
||||
while (i.hasPrevious()) {
|
||||
Map<String, AbstractConfigValue> m = Collections.<String, AbstractConfigValue> singletonMap(
|
||||
i.previous(), o);
|
||||
o = new SimpleConfigObject(value.origin(), m);
|
||||
o = new SimpleConfigObject(value.origin().setComments(null), m);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
private Path parseKey(Token token) {
|
||||
private Path parseKey(TokenWithComments token) {
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
if (Tokens.isValueWithType(token, ConfigValueType.STRING)) {
|
||||
String key = (String) Tokens.getValue(token).unwrapped();
|
||||
if (Tokens.isValueWithType(token.token, ConfigValueType.STRING)) {
|
||||
String key = (String) Tokens.getValue(token.token).unwrapped();
|
||||
return Path.newKey(key);
|
||||
} else {
|
||||
throw parseError(addKeyName("Expecting close brace } or a field name here, got "
|
||||
|
|
@ -364,9 +481,9 @@ final class Parser {
|
|||
}
|
||||
} else {
|
||||
List<Token> expression = new ArrayList<Token>();
|
||||
Token t = token;
|
||||
while (Tokens.isValue(t) || Tokens.isUnquotedText(t)) {
|
||||
expression.add(t);
|
||||
TokenWithComments t = token;
|
||||
while (Tokens.isValue(t.token) || Tokens.isUnquotedText(t.token)) {
|
||||
expression.add(t.token);
|
||||
t = nextToken(); // note: don't cross a newline
|
||||
}
|
||||
|
||||
|
|
@ -400,13 +517,13 @@ final class Parser {
|
|||
}
|
||||
|
||||
private void parseInclude(Map<String, AbstractConfigValue> values) {
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
while (isUnquotedWhitespace(t)) {
|
||||
TokenWithComments t = nextTokenIgnoringNewline();
|
||||
while (isUnquotedWhitespace(t.token)) {
|
||||
t = nextTokenIgnoringNewline();
|
||||
}
|
||||
|
||||
if (Tokens.isValueWithType(t, ConfigValueType.STRING)) {
|
||||
String name = (String) Tokens.getValue(t).unwrapped();
|
||||
if (Tokens.isValueWithType(t.token, ConfigValueType.STRING)) {
|
||||
String name = (String) Tokens.getValue(t.token).unwrapped();
|
||||
AbstractConfigObject obj = (AbstractConfigObject) includer
|
||||
.include(includeContext, name);
|
||||
|
||||
|
|
@ -448,8 +565,8 @@ final class Parser {
|
|||
boolean lastInsideEquals = false;
|
||||
|
||||
while (true) {
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.CLOSE_CURLY) {
|
||||
TokenWithComments t = nextTokenIgnoringNewline();
|
||||
if (t.token == Tokens.CLOSE_CURLY) {
|
||||
if (flavor == ConfigSyntax.JSON && afterComma) {
|
||||
throw parseError(addQuoteSuggestion(t.toString(),
|
||||
"expecting a field name after a comma, got a close brace } instead"));
|
||||
|
|
@ -458,45 +575,45 @@ final class Parser {
|
|||
"unbalanced close brace '}' with no open brace"));
|
||||
}
|
||||
break;
|
||||
} else if (t == Tokens.END && !hadOpenCurly) {
|
||||
} else if (t.token == Tokens.END && !hadOpenCurly) {
|
||||
putBack(t);
|
||||
break;
|
||||
} else if (flavor != ConfigSyntax.JSON && isIncludeKeyword(t)) {
|
||||
} else if (flavor != ConfigSyntax.JSON && isIncludeKeyword(t.token)) {
|
||||
parseInclude(values);
|
||||
|
||||
afterComma = false;
|
||||
} else {
|
||||
Path path = parseKey(t);
|
||||
Token afterKey = nextTokenIgnoringNewline();
|
||||
TokenWithComments keyToken = t;
|
||||
Path path = parseKey(keyToken);
|
||||
TokenWithComments afterKey = nextTokenIgnoringNewline();
|
||||
boolean insideEquals = false;
|
||||
|
||||
// path must be on-stack while we parse the value
|
||||
pathStack.push(path);
|
||||
|
||||
Token valueToken;
|
||||
TokenWithComments valueToken;
|
||||
AbstractConfigValue newValue;
|
||||
if (flavor == ConfigSyntax.CONF
|
||||
&& afterKey == Tokens.OPEN_CURLY) {
|
||||
if (flavor == ConfigSyntax.CONF && afterKey.token == Tokens.OPEN_CURLY) {
|
||||
// can omit the ':' or '=' before an object value
|
||||
valueToken = afterKey;
|
||||
newValue = parseObject(true);
|
||||
} else {
|
||||
if (!isKeyValueSeparatorToken(afterKey)) {
|
||||
if (!isKeyValueSeparatorToken(afterKey.token)) {
|
||||
throw parseError(addQuoteSuggestion(afterKey.toString(),
|
||||
"Key '" + path.render() + "' may not be followed by token: "
|
||||
+ afterKey));
|
||||
}
|
||||
|
||||
if (afterKey == Tokens.EQUALS) {
|
||||
if (afterKey.token == Tokens.EQUALS) {
|
||||
insideEquals = true;
|
||||
equalsCount += 1;
|
||||
}
|
||||
|
||||
consolidateValueTokens();
|
||||
valueToken = nextTokenIgnoringNewline();
|
||||
newValue = parseValue(valueToken);
|
||||
}
|
||||
|
||||
newValue = parseValue(valueToken.prepend(keyToken.comments));
|
||||
|
||||
lastPath = pathStack.pop();
|
||||
if (insideEquals) {
|
||||
equalsCount -= 1;
|
||||
|
|
@ -547,7 +664,7 @@ final class Parser {
|
|||
afterComma = true;
|
||||
} else {
|
||||
t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.CLOSE_CURLY) {
|
||||
if (t.token == Tokens.CLOSE_CURLY) {
|
||||
if (!hadOpenCurly) {
|
||||
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||
t.toString(), "unbalanced close brace '}' with no open brace"));
|
||||
|
|
@ -557,7 +674,7 @@ final class Parser {
|
|||
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||
t.toString(), "Expecting close brace } or a comma, got " + t));
|
||||
} else {
|
||||
if (t == Tokens.END) {
|
||||
if (t.token == Tokens.END) {
|
||||
putBack(t);
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -567,6 +684,7 @@ final class Parser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleConfigObject(objectOrigin, values);
|
||||
}
|
||||
|
||||
|
|
@ -577,18 +695,15 @@ final class Parser {
|
|||
|
||||
consolidateValueTokens();
|
||||
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
TokenWithComments t = nextTokenIgnoringNewline();
|
||||
|
||||
// special-case the first element
|
||||
if (t == Tokens.CLOSE_SQUARE) {
|
||||
if (t.token == Tokens.CLOSE_SQUARE) {
|
||||
return new SimpleConfigList(arrayOrigin,
|
||||
Collections.<AbstractConfigValue> emptyList());
|
||||
} else if (Tokens.isValue(t)) {
|
||||
} else if (Tokens.isValue(t.token) || t.token == Tokens.OPEN_CURLY
|
||||
|| t.token == Tokens.OPEN_SQUARE) {
|
||||
values.add(parseValue(t));
|
||||
} else if (t == Tokens.OPEN_CURLY) {
|
||||
values.add(parseObject(true));
|
||||
} else if (t == Tokens.OPEN_SQUARE) {
|
||||
values.add(parseArray());
|
||||
} else {
|
||||
throw parseError(addKeyName("List should have ] or a first element after the open [, instead had token: "
|
||||
+ t
|
||||
|
|
@ -604,7 +719,7 @@ final class Parser {
|
|||
// comma (or newline equivalent) consumed
|
||||
} else {
|
||||
t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.CLOSE_SQUARE) {
|
||||
if (t.token == Tokens.CLOSE_SQUARE) {
|
||||
return new SimpleConfigList(arrayOrigin, values);
|
||||
} else {
|
||||
throw parseError(addKeyName("List should have ended with ] or had a comma, instead had token: "
|
||||
|
|
@ -619,14 +734,10 @@ final class Parser {
|
|||
consolidateValueTokens();
|
||||
|
||||
t = nextTokenIgnoringNewline();
|
||||
if (Tokens.isValue(t)) {
|
||||
if (Tokens.isValue(t.token) || t.token == Tokens.OPEN_CURLY
|
||||
|| t.token == Tokens.OPEN_SQUARE) {
|
||||
values.add(parseValue(t));
|
||||
} else if (t == Tokens.OPEN_CURLY) {
|
||||
values.add(parseObject(true));
|
||||
} else if (t == Tokens.OPEN_SQUARE) {
|
||||
values.add(parseArray());
|
||||
} else if (flavor != ConfigSyntax.JSON
|
||||
&& t == Tokens.CLOSE_SQUARE) {
|
||||
} else if (flavor != ConfigSyntax.JSON && t.token == Tokens.CLOSE_SQUARE) {
|
||||
// we allow one trailing comma
|
||||
putBack(t);
|
||||
} else {
|
||||
|
|
@ -640,8 +751,8 @@ final class Parser {
|
|||
}
|
||||
|
||||
AbstractConfigValue parse() {
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.START) {
|
||||
TokenWithComments t = nextTokenIgnoringNewline();
|
||||
if (t.token == Tokens.START) {
|
||||
// OK
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
|
|
@ -650,13 +761,11 @@ final class Parser {
|
|||
|
||||
t = nextTokenIgnoringNewline();
|
||||
AbstractConfigValue result = null;
|
||||
if (t == Tokens.OPEN_CURLY) {
|
||||
result = parseObject(true);
|
||||
} else if (t == Tokens.OPEN_SQUARE) {
|
||||
result = parseArray();
|
||||
if (t.token == Tokens.OPEN_CURLY || t.token == Tokens.OPEN_SQUARE) {
|
||||
result = parseValue(t);
|
||||
} else {
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
if (t == Tokens.END) {
|
||||
if (t.token == Tokens.END) {
|
||||
throw parseError("Empty document");
|
||||
} else {
|
||||
throw parseError("Document must have an object or array at root, unexpected token: "
|
||||
|
|
@ -668,11 +777,14 @@ final class Parser {
|
|||
// of it, so put it back.
|
||||
putBack(t);
|
||||
result = parseObject(false);
|
||||
// in this case we don't try to use commentsStack comments
|
||||
// since they would all presumably apply to fields not the
|
||||
// root object
|
||||
}
|
||||
}
|
||||
|
||||
t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.END) {
|
||||
if (t.token == Tokens.END) {
|
||||
return result;
|
||||
} else {
|
||||
throw parseError("Document has trailing tokens after first object or array: "
|
||||
|
|
|
|||
|
|
@ -145,6 +145,14 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
|||
sb.append("# ");
|
||||
sb.append(v.origin().description());
|
||||
sb.append("\n");
|
||||
|
||||
for (String comment : v.origin().comments()) {
|
||||
indent(sb, indent + 1);
|
||||
sb.append("# ");
|
||||
sb.append(comment);
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
indent(sb, indent + 1);
|
||||
}
|
||||
v.render(sb, indent + 1, formatted);
|
||||
|
|
@ -353,4 +361,9 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
|||
public ConfigValue set(int index, ConfigValue element) {
|
||||
throw weAreImmutable("set");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SimpleConfigList newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return new SimpleConfigList(newOrigin, value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ final class SimpleConfigObject extends AbstractConfigObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected SimpleConfigObject newCopy(ResolveStatus newStatus, boolean newIgnoresFallbacks) {
|
||||
return new SimpleConfigObject(origin(), value, newStatus, newIgnoresFallbacks);
|
||||
protected SimpleConfigObject newCopy(ResolveStatus newStatus, boolean newIgnoresFallbacks,
|
||||
ConfigOrigin newOrigin) {
|
||||
return new SimpleConfigObject(newOrigin, value, newStatus, newIgnoresFallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import java.net.MalformedURLException;
|
|||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -22,19 +23,21 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
final private int endLineNumber;
|
||||
final private OriginType originType;
|
||||
final private String urlOrNull;
|
||||
final private List<String> commentsOrNull;
|
||||
|
||||
protected SimpleConfigOrigin(String description, int lineNumber, int endLineNumber,
|
||||
OriginType originType,
|
||||
String urlOrNull) {
|
||||
String urlOrNull, List<String> commentsOrNull) {
|
||||
this.description = description;
|
||||
this.lineNumber = lineNumber;
|
||||
this.endLineNumber = endLineNumber;
|
||||
this.originType = originType;
|
||||
this.urlOrNull = urlOrNull;
|
||||
this.commentsOrNull = commentsOrNull;
|
||||
}
|
||||
|
||||
static SimpleConfigOrigin newSimple(String description) {
|
||||
return new SimpleConfigOrigin(description, -1, -1, OriginType.GENERIC, null);
|
||||
return new SimpleConfigOrigin(description, -1, -1, OriginType.GENERIC, null, null);
|
||||
}
|
||||
|
||||
static SimpleConfigOrigin newFile(String filename) {
|
||||
|
|
@ -44,17 +47,17 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
} catch (MalformedURLException e) {
|
||||
url = null;
|
||||
}
|
||||
return new SimpleConfigOrigin(filename, -1, -1, OriginType.FILE, url);
|
||||
return new SimpleConfigOrigin(filename, -1, -1, OriginType.FILE, url, null);
|
||||
}
|
||||
|
||||
static SimpleConfigOrigin newURL(URL url) {
|
||||
String u = url.toExternalForm();
|
||||
return new SimpleConfigOrigin(u, -1, -1, OriginType.URL, u);
|
||||
return new SimpleConfigOrigin(u, -1, -1, OriginType.URL, u, null);
|
||||
}
|
||||
|
||||
static SimpleConfigOrigin newResource(String resource, URL url) {
|
||||
return new SimpleConfigOrigin(resource, -1, -1, OriginType.RESOURCE,
|
||||
url != null ? url.toExternalForm() : null);
|
||||
url != null ? url.toExternalForm() : null, null);
|
||||
}
|
||||
|
||||
static SimpleConfigOrigin newResource(String resource) {
|
||||
|
|
@ -66,13 +69,22 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
return this;
|
||||
} else {
|
||||
return new SimpleConfigOrigin(this.description, lineNumber, lineNumber,
|
||||
this.originType, this.urlOrNull);
|
||||
this.originType, this.urlOrNull, this.commentsOrNull);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleConfigOrigin addURL(URL url) {
|
||||
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType,
|
||||
url != null ? url.toExternalForm() : null);
|
||||
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber,
|
||||
this.originType, url != null ? url.toExternalForm() : null, this.commentsOrNull);
|
||||
}
|
||||
|
||||
SimpleConfigOrigin setComments(List<String> comments) {
|
||||
if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull)) {
|
||||
return this;
|
||||
} else {
|
||||
return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber,
|
||||
this.originType, this.urlOrNull, comments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -172,12 +184,22 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
return lineNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> comments() {
|
||||
if (commentsOrNull != null) {
|
||||
return commentsOrNull;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
static final String MERGE_OF_PREFIX = "merge of ";
|
||||
|
||||
private static SimpleConfigOrigin mergeTwo(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||
String mergedDesc;
|
||||
int mergedStartLine;
|
||||
int mergedEndLine;
|
||||
List<String> mergedComments;
|
||||
|
||||
OriginType mergedType;
|
||||
if (a.originType == b.originType) {
|
||||
|
|
@ -233,8 +255,18 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
mergedURL = null;
|
||||
}
|
||||
|
||||
if (ConfigImplUtil.equalsHandlingNull(a.commentsOrNull, b.commentsOrNull)) {
|
||||
mergedComments = a.commentsOrNull;
|
||||
} else {
|
||||
mergedComments = new ArrayList<String>();
|
||||
if (a.commentsOrNull != null)
|
||||
mergedComments.addAll(a.commentsOrNull);
|
||||
if (b.commentsOrNull != null)
|
||||
mergedComments.addAll(b.commentsOrNull);
|
||||
}
|
||||
|
||||
return new SimpleConfigOrigin(mergedDesc, mergedStartLine, mergedEndLine, mergedType,
|
||||
mergedURL);
|
||||
mergedURL, mergedComments);
|
||||
}
|
||||
|
||||
private static int similarity(SimpleConfigOrigin a, SimpleConfigOrigin b) {
|
||||
|
|
|
|||
|
|
@ -17,5 +17,6 @@ enum TokenType {
|
|||
NEWLINE,
|
||||
UNQUOTED_TEXT,
|
||||
SUBSTITUTION,
|
||||
PROBLEM;
|
||||
PROBLEM,
|
||||
COMMENT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,40 +168,27 @@ final class Tokenizer {
|
|||
return c != '\n' && ConfigImplUtil.isWhitespace(c);
|
||||
}
|
||||
|
||||
private int slurpComment() {
|
||||
for (;;) {
|
||||
int c = nextCharRaw();
|
||||
if (c == -1 || c == '\n') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get next char, skipping comments
|
||||
private int nextCharSkippingComments() {
|
||||
for (;;) {
|
||||
int c = nextCharRaw();
|
||||
|
||||
private boolean startOfComment(int c) {
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
return false;
|
||||
} else {
|
||||
if (allowComments) {
|
||||
if (c == '#') {
|
||||
return slurpComment();
|
||||
return true;
|
||||
} else if (c == '/') {
|
||||
int maybeSecondSlash = nextCharRaw();
|
||||
if (maybeSecondSlash == '/') {
|
||||
return slurpComment();
|
||||
} else {
|
||||
// we want to predictably NOT consume any chars
|
||||
putBack(maybeSecondSlash);
|
||||
return c;
|
||||
if (maybeSecondSlash == '/') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return c;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -209,7 +196,7 @@ final class Tokenizer {
|
|||
// get next char, skipping non-newline whitespace
|
||||
private int nextCharAfterWhitespace(WhitespaceSaver saver) {
|
||||
for (;;) {
|
||||
int c = nextCharSkippingComments();
|
||||
int c = nextCharRaw();
|
||||
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
|
|
@ -269,6 +256,27 @@ final class Tokenizer {
|
|||
return ((SimpleConfigOrigin) baseOrigin).setLineNumber(lineNumber);
|
||||
}
|
||||
|
||||
// ONE char has always been consumed, either the # or the first /, but
|
||||
// not both slashes
|
||||
private Token pullComment(int firstChar) {
|
||||
if (firstChar == '/') {
|
||||
int discard = nextCharRaw();
|
||||
if (discard != '/')
|
||||
throw new ConfigException.BugOrBroken("called pullComment but // not seen");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
int c = nextCharRaw();
|
||||
if (c == -1 || c == '\n') {
|
||||
putBack(c);
|
||||
return Tokens.newComment(lineOrigin, sb.toString());
|
||||
} else {
|
||||
sb.appendCodePoint(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// chars JSON allows a number to start with
|
||||
static final String firstNumberChars = "0123456789-";
|
||||
// chars JSON allows to be part of a number
|
||||
|
|
@ -283,7 +291,7 @@ final class Tokenizer {
|
|||
private Token pullUnquotedText() {
|
||||
ConfigOrigin origin = lineOrigin;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c = nextCharSkippingComments();
|
||||
int c = nextCharRaw();
|
||||
while (true) {
|
||||
if (c == -1) {
|
||||
break;
|
||||
|
|
@ -291,6 +299,8 @@ final class Tokenizer {
|
|||
break;
|
||||
} else if (isWhitespace(c)) {
|
||||
break;
|
||||
} else if (startOfComment(c)) {
|
||||
break;
|
||||
} else {
|
||||
sb.appendCodePoint(c);
|
||||
}
|
||||
|
|
@ -310,7 +320,7 @@ final class Tokenizer {
|
|||
return Tokens.newBoolean(origin, false);
|
||||
}
|
||||
|
||||
c = nextCharSkippingComments();
|
||||
c = nextCharRaw();
|
||||
}
|
||||
|
||||
// put back the char that ended the unquoted text
|
||||
|
|
@ -324,12 +334,12 @@ final class Tokenizer {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
sb.appendCodePoint(firstChar);
|
||||
boolean containedDecimalOrE = false;
|
||||
int c = nextCharSkippingComments();
|
||||
int c = nextCharRaw();
|
||||
while (c != -1 && numberChars.indexOf(c) >= 0) {
|
||||
if (c == '.' || c == 'e' || c == 'E')
|
||||
containedDecimalOrE = true;
|
||||
sb.appendCodePoint(c);
|
||||
c = nextCharSkippingComments();
|
||||
c = nextCharRaw();
|
||||
}
|
||||
// the last character we looked at wasn't part of the number, put it
|
||||
// back
|
||||
|
|
@ -382,7 +392,7 @@ final class Tokenizer {
|
|||
// kind of absurdly slow, but screw it for now
|
||||
char[] a = new char[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int c = nextCharSkippingComments();
|
||||
int c = nextCharRaw();
|
||||
if (c == -1)
|
||||
throw problem("End of input but expecting 4 hex digits for \\uXXXX escape");
|
||||
a[i] = (char) c;
|
||||
|
|
@ -431,14 +441,14 @@ final class Tokenizer {
|
|||
private Token pullSubstitution() throws ProblemException {
|
||||
// the initial '$' has already been consumed
|
||||
ConfigOrigin origin = lineOrigin;
|
||||
int c = nextCharSkippingComments();
|
||||
int c = nextCharRaw();
|
||||
if (c != '{') {
|
||||
throw problem(asString(c), "'$' not followed by {, '" + asString(c)
|
||||
+ "' not allowed after '$'", true /* suggestQuotes */);
|
||||
}
|
||||
|
||||
boolean optional = false;
|
||||
c = nextCharSkippingComments();
|
||||
c = nextCharRaw();
|
||||
if (c == '?') {
|
||||
optional = true;
|
||||
} else {
|
||||
|
|
@ -484,6 +494,9 @@ final class Tokenizer {
|
|||
return line;
|
||||
} else {
|
||||
Token t = null;
|
||||
if (startOfComment(c)) {
|
||||
t = pullComment(c);
|
||||
} else {
|
||||
switch (c) {
|
||||
case '"':
|
||||
t = pullQuotedString();
|
||||
|
|
@ -525,6 +538,7 @@ final class Tokenizer {
|
|||
t = pullUnquotedText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t == null)
|
||||
throw new ConfigException.BugOrBroken(
|
||||
|
|
@ -548,6 +562,7 @@ final class Tokenizer {
|
|||
Token whitespace = whitespaceSaver.check(t, origin, lineNumber);
|
||||
if (whitespace != null)
|
||||
tokens.add(whitespace);
|
||||
|
||||
tokens.add(t);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ final class Tokens {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "'\n'@" + lineNumber();
|
||||
return "'\\n'@" + lineNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -167,6 +167,45 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
static private class Comment extends Token {
|
||||
final private String text;
|
||||
|
||||
Comment(ConfigOrigin origin, String text) {
|
||||
super(TokenType.COMMENT, origin);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
String text() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("'#");
|
||||
sb.append(text);
|
||||
sb.append("' (COMMENT)");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof Comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && ((Comment) other).text.equals(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 41 * (41 + super.hashCode());
|
||||
h = 41 * (h + text.hashCode());
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a Value, because it requires special processing
|
||||
static private class Substitution extends Token {
|
||||
final private boolean optional;
|
||||
|
|
@ -262,6 +301,18 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
static boolean isComment(Token token) {
|
||||
return token instanceof Comment;
|
||||
}
|
||||
|
||||
static String getCommentText(Token token) {
|
||||
if (token instanceof Comment) {
|
||||
return ((Comment) token).text();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken("tried to get comment text from " + token);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isUnquotedText(Token token) {
|
||||
return token instanceof UnquotedText;
|
||||
}
|
||||
|
|
@ -316,6 +367,10 @@ final class Tokens {
|
|||
return new Problem(origin, what, message, suggestQuotes, cause);
|
||||
}
|
||||
|
||||
static Token newComment(ConfigOrigin origin, String text) {
|
||||
return new Comment(origin, text);
|
||||
}
|
||||
|
||||
static Token newUnquotedText(ConfigOrigin origin, String s) {
|
||||
return new UnquotedText(origin, s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,53 +3,64 @@
|
|||
##############################
|
||||
|
||||
# This the reference config file has all the default settings.
|
||||
# Make your edits/overrides in your akka.conf.
|
||||
# Make your edits/overrides in your application.conf.
|
||||
|
||||
akka {
|
||||
version = "2.0-SNAPSHOT" # Akka version, checked against the runtime version of Akka.
|
||||
# Akka version, checked against the runtime version of Akka.
|
||||
version = "2.0-SNAPSHOT"
|
||||
|
||||
home = "" # Home directory of Akka, modules in the deploy directory will be loaded
|
||||
# Home directory of Akka, modules in the deploy directory will be loaded
|
||||
home = ""
|
||||
|
||||
enabled-modules = [] # Comma separated list of the enabled modules. Options: ["cluster", "camel", "http"]
|
||||
# Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT)
|
||||
event-handlers = ["akka.event.Logging$DefaultLogger"]
|
||||
|
||||
event-handlers = ["akka.event.Logging$DefaultLogger"] # Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT)
|
||||
loglevel = "INFO" # Options: ERROR, WARNING, INFO, DEBUG
|
||||
# this level is used by the configured loggers (see "event-handlers") as soon
|
||||
# Log level used by the configured loggers (see "event-handlers") as soon
|
||||
# as they have been started; before that, see "stdout-loglevel"
|
||||
stdout-loglevel = "WARNING" # Loglevel for the very basic logger activated during AkkaApplication startup
|
||||
# FIXME: Is there any sensible reason why we have 2 different log levels?
|
||||
# Options: ERROR, WARNING, INFO, DEBUG
|
||||
loglevel = "INFO"
|
||||
|
||||
logConfigOnStart = off # Log the complete configuration at INFO level when the actor system is started.
|
||||
# Log level for the very basic logger activated during AkkaApplication startup
|
||||
# Options: ERROR, WARNING, INFO, DEBUG
|
||||
stdout-loglevel = "WARNING"
|
||||
|
||||
# Log the complete configuration at INFO level when the actor system is started.
|
||||
# This is useful when you are uncertain of what configuration is used.
|
||||
logConfigOnStart = off
|
||||
|
||||
extensions = [] # List FQCN of extensions which shall be loaded at actor system startup.
|
||||
# List FQCN of extensions which shall be loaded at actor system startup.
|
||||
# FIXME: clarify "extensions" here, "Akka Extensions (<link to docs>)"
|
||||
|
||||
# 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.rest.java.Boot",
|
||||
# "sample.rest.scala.Boot",
|
||||
# "sample.security.Boot"]
|
||||
boot = []
|
||||
extensions = []
|
||||
|
||||
actor {
|
||||
|
||||
provider = "akka.actor.LocalActorRefProvider"
|
||||
creation-timeout = 20s # Timeout for ActorSystem.actorOf
|
||||
reaper-interval = 5s # frequency with which stopping actors are prodded in case they had to be removed from their parents
|
||||
timeout = 5s # Default timeout for Future based invocations
|
||||
|
||||
# Timeout for ActorSystem.actorOf
|
||||
creation-timeout = 20s
|
||||
|
||||
# frequency with which stopping actors are prodded in case they had to be removed from their parents
|
||||
reaper-interval = 5s
|
||||
|
||||
# Default timeout for Future based invocations
|
||||
# - Actor: ask && ?
|
||||
# - UntypedActor: ask
|
||||
# - TypedActor: methods with non-void return type
|
||||
serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability
|
||||
dispatcher-shutdown-timeout = 1s # How long dispatchers by default will wait for new actors until they shut down
|
||||
timeout = 5s
|
||||
|
||||
# Does a deep clone of (non-primitive) messages to ensure immutability
|
||||
serialize-messages = off
|
||||
|
||||
# How long dispatchers by default will wait for new actors until they shut down
|
||||
dispatcher-shutdown-timeout = 1s
|
||||
|
||||
deployment {
|
||||
|
||||
default { # deployment id pattern, e.g. /app/service-ping
|
||||
# deployment id pattern, e.g. /user/service-ping
|
||||
default {
|
||||
|
||||
router = "direct" # routing (load-balance) scheme to use
|
||||
|
||||
# routing (load-balance) scheme to use
|
||||
# available: "direct", "round-robin", "random", "scatter-gather"
|
||||
# or: fully qualified class name of the router class
|
||||
# default is "direct";
|
||||
|
|
@ -59,65 +70,113 @@ akka {
|
|||
# supplied in the source code (overridable using create-as below)
|
||||
# - target.paths: will look the paths up using actorFor and route to
|
||||
# them, i.e. will not create children
|
||||
router = "direct"
|
||||
|
||||
nr-of-instances = 1 # number of children to create in case of a non-direct router; this setting
|
||||
# number of children to create in case of a non-direct router; this setting
|
||||
# is ignored if target.paths is given
|
||||
nr-of-instances = 1
|
||||
|
||||
create-as { # FIXME document 'create-as'
|
||||
class = "" # fully qualified class name of recipe implementation
|
||||
# FIXME document 'create-as', ticket 1511
|
||||
create-as {
|
||||
# fully qualified class name of recipe implementation
|
||||
class = ""
|
||||
}
|
||||
|
||||
target {
|
||||
paths = [] # Alternatively to giving nr-of-instances you can specify the full paths of
|
||||
# Alternatively to giving nr-of-instances you can specify the full paths of
|
||||
# those actors which should be routed to. This setting takes precedence over
|
||||
# nr-of-instances
|
||||
paths = []
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
default-dispatcher {
|
||||
type = "Dispatcher" # Must be one of the following
|
||||
# Must be one of the following
|
||||
# Dispatcher, (BalancingDispatcher, only valid when all actors using it are of the same type),
|
||||
# A FQCN to a class inheriting MessageDispatcherConfigurator with a no-arg visible constructor
|
||||
name = "DefaultDispatcher" # Name used in log messages and thread names.
|
||||
daemonic = off # Toggles whether the threads created by this dispatcher should be daemons or not
|
||||
keep-alive-time = 60s # Keep alive time for threads
|
||||
core-pool-size-min = 8 # minimum number of threads to cap factor-based core number to
|
||||
core-pool-size-factor = 8.0 # No of core threads ... ceil(available processors * factor)
|
||||
core-pool-size-max = 4096 # maximum number of threads to cap factor-based number to
|
||||
type = "Dispatcher"
|
||||
|
||||
# Name used in log messages and thread names.
|
||||
name = "DefaultDispatcher"
|
||||
|
||||
# Toggles whether the threads created by this dispatcher should be daemons or not
|
||||
daemonic = off
|
||||
|
||||
# Keep alive time for threads
|
||||
keep-alive-time = 60s
|
||||
|
||||
# minimum number of threads to cap factor-based core number to
|
||||
core-pool-size-min = 8
|
||||
|
||||
# No of core threads ... ceil(available processors * factor)
|
||||
core-pool-size-factor = 8.0
|
||||
|
||||
# maximum number of threads to cap factor-based number to
|
||||
core-pool-size-max = 4096
|
||||
|
||||
# Hint: max-pool-size is only used for bounded task queues
|
||||
max-pool-size-min = 8 # minimum number of threads to cap factor-based max number to
|
||||
max-pool-size-factor = 8.0 # Max no of threads ... ceil(available processors * factor)
|
||||
max-pool-size-max = 4096 # maximum number of threads to cap factor-based max number to
|
||||
task-queue-size = -1 # Specifies the bounded capacity of the task queue (< 1 == unbounded)
|
||||
task-queue-type = "linked" # Specifies which type of task queue will be used, can be "array" or "linked" (default)
|
||||
allow-core-timeout = on # Allow core threads to time out
|
||||
throughput = 5 # Throughput defines the number of messages that are processed in a batch before the
|
||||
# minimum number of threads to cap factor-based max number to
|
||||
max-pool-size-min = 8
|
||||
|
||||
# Max no of threads ... ceil(available processors * factor)
|
||||
max-pool-size-factor = 8.0
|
||||
|
||||
# maximum number of threads to cap factor-based max number to
|
||||
max-pool-size-max = 4096
|
||||
|
||||
# Specifies the bounded capacity of the task queue (< 1 == unbounded)
|
||||
task-queue-size = -1
|
||||
|
||||
# Specifies which type of task queue will be used, can be "array" or "linked" (default)
|
||||
task-queue-type = "linked"
|
||||
|
||||
# Allow core threads to time out
|
||||
allow-core-timeout = on
|
||||
|
||||
# Throughput defines the number of messages that are processed in a batch before the
|
||||
# thread is returned to the pool. Set to 1 for as fair as possible.
|
||||
throughput-deadline-time = 0ms # Throughput deadline for Dispatcher, set to 0 or negative for no deadline
|
||||
mailbox-capacity = -1 # If negative (or zero) then an unbounded mailbox is used (default)
|
||||
throughput = 5
|
||||
|
||||
# Throughput deadline for Dispatcher, set to 0 or negative for no deadline
|
||||
throughput-deadline-time = 0ms
|
||||
|
||||
# If negative (or zero) then an unbounded mailbox is used (default)
|
||||
# If positive then a bounded mailbox is used and the capacity is set using the property
|
||||
# NOTE: setting a mailbox to 'blocking' can be a bit dangerous, could lead to deadlock, use with care
|
||||
# The following are only used for Dispatcher and only if mailbox-capacity > 0
|
||||
mailbox-push-timeout-time = 10s # Specifies the timeout to add a new message to a mailbox that is full - negative number means infinite timeout
|
||||
mailbox-capacity = -1
|
||||
|
||||
# Specifies the timeout to add a new message to a mailbox that is full -
|
||||
# negative number means infinite timeout
|
||||
mailbox-push-timeout-time = 10s
|
||||
}
|
||||
|
||||
debug {
|
||||
receive = off # enable function of Actor.loggable(), which is to log any received message at DEBUG level
|
||||
autoreceive = off # enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill and the like)
|
||||
lifecycle = off # enable DEBUG logging of actor lifecycle changes
|
||||
fsm = off # enable DEBUG logging of all LoggingFSMs for events, transitions and timers
|
||||
event-stream = off # enable DEBUG logging of subscription changes on the eventStream
|
||||
# enable function of Actor.loggable(), which is to log any received message at DEBUG level
|
||||
receive = off
|
||||
|
||||
# enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill and the like)
|
||||
autoreceive = off
|
||||
|
||||
# enable DEBUG logging of actor lifecycle changes
|
||||
lifecycle = off
|
||||
|
||||
# enable DEBUG logging of all LoggingFSMs for events, transitions and timers
|
||||
fsm = off
|
||||
|
||||
# enable DEBUG logging of subscription changes on the eventStream
|
||||
event-stream = off
|
||||
}
|
||||
|
||||
# Entries for pluggable serializers and their bindings. If a binding for a specific class is not found,
|
||||
# then the default serializer (Java serialization) is used.
|
||||
#
|
||||
serializers {
|
||||
# java = "akka.serialization.JavaSerializer"
|
||||
# proto = "akka.testing.ProtobufSerializer"
|
||||
# sjson = "akka.testing.SJSONSerializer"
|
||||
|
||||
default = "akka.serialization.JavaSerializer"
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +197,6 @@ akka {
|
|||
#
|
||||
scheduler {
|
||||
# The HashedWheelTimer (HWT) implementation from Netty is used as the default scheduler in the system.
|
||||
#
|
||||
# HWT does not execute the scheduled tasks on exact time.
|
||||
# It will, on every tick, check if there are any tasks behind the schedule and execute them.
|
||||
# You can increase or decrease the accuracy of the execution timing by specifying smaller or larger tick duration.
|
||||
|
|
|
|||
|
|
@ -113,32 +113,6 @@ object Status {
|
|||
case class Failure(cause: Throwable) extends Status
|
||||
}
|
||||
|
||||
case class Timeout(duration: Duration) {
|
||||
def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
|
||||
def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
|
||||
}
|
||||
|
||||
object Timeout {
|
||||
/**
|
||||
* A timeout with zero duration, will cause most requests to always timeout.
|
||||
*/
|
||||
val zero = new Timeout(Duration.Zero)
|
||||
|
||||
/**
|
||||
* A Timeout with infinite duration. Will never timeout. Use extreme caution with this
|
||||
* as it may cause memory leaks, blocked threads, or may not even be supported by
|
||||
* the receiver, which would result in an exception.
|
||||
*/
|
||||
val never = new Timeout(Duration.Inf)
|
||||
|
||||
def apply(timeout: Long) = new Timeout(timeout)
|
||||
def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit)
|
||||
|
||||
implicit def durationToTimeout(duration: Duration) = new Timeout(duration)
|
||||
implicit def intToTimeout(timeout: Int) = new Timeout(timeout)
|
||||
implicit def longToTimeout(timeout: Long) = new Timeout(timeout)
|
||||
}
|
||||
|
||||
trait ActorLogging { this: Actor ⇒
|
||||
val log = akka.event.Logging(context.system.eventStream, context.self)
|
||||
}
|
||||
|
|
@ -307,7 +281,8 @@ trait Actor {
|
|||
// =========================================
|
||||
|
||||
private[akka] final def apply(msg: Any) = {
|
||||
val behaviorStack = context.hotswap
|
||||
// FIXME this should all go into ActorCell
|
||||
val behaviorStack = context.asInstanceOf[ActorCell].hotswap
|
||||
msg match {
|
||||
case msg if behaviorStack.nonEmpty && behaviorStack.head.isDefinedAt(msg) ⇒ behaviorStack.head.apply(msg)
|
||||
case msg if behaviorStack.isEmpty && processingBehavior.isDefinedAt(msg) ⇒ processingBehavior.apply(msg)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ trait ActorContext extends ActorRefFactory {
|
|||
def setReceiveTimeout(timeout: Duration): Unit
|
||||
|
||||
/**
|
||||
* Resets the current receive timeout.
|
||||
* Clears the receive timeout, i.e. deactivates this feature.
|
||||
*/
|
||||
def resetReceiveTimeout(): Unit
|
||||
|
||||
|
|
@ -83,16 +83,6 @@ trait ActorContext extends ActorRefFactory {
|
|||
*/
|
||||
def unbecome(): Unit
|
||||
|
||||
/**
|
||||
* Returns the current message envelope.
|
||||
*/
|
||||
def currentMessage: Envelope
|
||||
|
||||
/**
|
||||
* Returns a stack with the hotswapped behaviors (as Scala PartialFunction).
|
||||
*/
|
||||
def hotswap: Stack[PartialFunction[Any, Unit]]
|
||||
|
||||
/**
|
||||
* Returns the sender 'ActorRef' of the current message.
|
||||
*/
|
||||
|
|
@ -109,10 +99,6 @@ trait ActorContext extends ActorRefFactory {
|
|||
*/
|
||||
implicit def dispatcher: MessageDispatcher
|
||||
|
||||
def handleFailure(child: ActorRef, cause: Throwable): Unit
|
||||
|
||||
def handleChildTerminated(child: ActorRef): Unit
|
||||
|
||||
/**
|
||||
* The system that the actor belongs to.
|
||||
* Importing this member will place a implicit MessageDispatcher in scope.
|
||||
|
|
|
|||
|
|
@ -18,34 +18,57 @@ import akka.event.LoggingAdapter
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* ActorRef is an immutable and serializable handle to an Actor.
|
||||
* <p/>
|
||||
* Create an ActorRef for an Actor by using the factory method on the Actor object.
|
||||
* <p/>
|
||||
* Here is an example on how to create an actor with a default constructor.
|
||||
* <pre>
|
||||
* import Actor._
|
||||
* Immutable and serializable handle to an actor, which may or may not reside
|
||||
* on the local host or inside the same [[akka.actor.ActorSystem]]. An ActorRef
|
||||
* can be obtained from an [[akka.actor.ActorRefFactory]], an interface which
|
||||
* is implemented by ActorSystem and [[akka.actor.ActorContext]]. This means
|
||||
* actors can be created top-level in the ActorSystem or as children of an
|
||||
* existing actor, but only from within that actor.
|
||||
*
|
||||
* val actor = actorOf(Props[MyActor]
|
||||
* actor ! message
|
||||
* actor.stop()
|
||||
* </pre>
|
||||
* ActorRefs can be freely shared among actors by message passing. Message
|
||||
* passing conversely is their only purpose, as demonstrated in the following
|
||||
* examples:
|
||||
*
|
||||
* You can also create and start actors like this:
|
||||
* <pre>
|
||||
* val actor = actorOf(Props[MyActor]
|
||||
* </pre>
|
||||
* Scala:
|
||||
* {{{
|
||||
* class ExampleActor extends Actor {
|
||||
* val other = context.actorOf(Props[OtherActor], "childName") // will be destroyed and re-created upon restart by default
|
||||
*
|
||||
* Here is an example on how to create an actor with a non-default constructor.
|
||||
* <pre>
|
||||
* import Actor._
|
||||
* def receive {
|
||||
* case Request1(msg) => other ! refine(msg) // uses this actor as sender reference, reply goes to us
|
||||
* case Request2(msg) => other.tell(msg, sender) // forward sender reference, enabling direct reply
|
||||
* case Request3(msg) => sender ! (other ? msg) // will reply with a Future for holding other’s reply (implicit timeout from "akka.actor.timeout")
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* val actor = actorOf(Props(new MyActor(...))
|
||||
* actor ! message
|
||||
* actor.stop()
|
||||
* </pre>
|
||||
* Java:
|
||||
* {{{
|
||||
* public class ExampleActor Extends UntypedActor {
|
||||
* // this child will be destroyed and re-created upon restart by default
|
||||
* final ActorRef other = getContext().actorOf(new Props(OtherActor.class), "childName");
|
||||
*
|
||||
* The natural ordering of ActorRef is defined in terms of its [[akka.actor.ActorPath]].
|
||||
* @Override
|
||||
* public void onReceive(Object o) {
|
||||
* if (o instanceof Request1) {
|
||||
* val msg = ((Request1) o).getMsg();
|
||||
* other.tell(msg); // uses this actor as sender reference, reply goes to us
|
||||
*
|
||||
* } else if (o instanceof Request2) {
|
||||
* val msg = ((Request2) o).getMsg();
|
||||
* other.tell(msg, getSender()); // forward sender reference, enabling direct reply
|
||||
*
|
||||
* } else if (o instanceof Request3) {
|
||||
* val msg = ((Request3) o).getMsg();
|
||||
* getSender().tell(other.ask(msg, 5000)); // reply with Future for holding the other’s reply (timeout 5 seconds)
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* ActorRef does not have a method for terminating the actor it points to, use
|
||||
* [[akka.actor.ActorRefFactory]]`.stop(child)` for this purpose.
|
||||
*/
|
||||
abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable {
|
||||
scalaRef: InternalActorRef ⇒
|
||||
|
|
@ -340,10 +363,12 @@ case class SerializedActorRef(path: String) {
|
|||
|
||||
@throws(classOf[java.io.ObjectStreamException])
|
||||
def readResolve(): AnyRef = currentSystem.value match {
|
||||
case null ⇒ throw new IllegalStateException(
|
||||
case null ⇒
|
||||
throw new IllegalStateException(
|
||||
"Trying to deserialize a serialized ActorRef without an ActorSystem in scope." +
|
||||
" Use 'akka.serialization.Serialization.currentSystem.withValue(system) { ... }'")
|
||||
case someSystem ⇒ someSystem.actorFor(path)
|
||||
case someSystem ⇒
|
||||
someSystem.actorFor(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,8 +384,6 @@ trait MinimalActorRef extends InternalActorRef {
|
|||
else Nobody
|
||||
}
|
||||
|
||||
//FIXME REMOVE THIS, ticket #1416
|
||||
//FIXME REMOVE THIS, ticket #1415
|
||||
def suspend(): Unit = ()
|
||||
def resume(): Unit = ()
|
||||
|
||||
|
|
@ -485,7 +508,7 @@ class AskActorRef(
|
|||
}
|
||||
|
||||
override def ?(message: Any)(implicit timeout: Timeout): Future[Any] =
|
||||
Promise.failed(new UnsupportedOperationException("Ask/? is not supported for %s".format(getClass.getName)))(dispatcher)
|
||||
Promise.failed(new UnsupportedOperationException("Ask/? is not supported for [%s]".format(getClass.getName)))(dispatcher)
|
||||
|
||||
override def isTerminated = result.isCompleted
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@ package akka.actor
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import org.jboss.netty.akka.util.{ TimerTask, HashedWheelTimer }
|
||||
import akka.util.Timeout
|
||||
import akka.util.Timeout.intToTimeout
|
||||
import akka.config.ConfigurationException
|
||||
import akka.dispatch._
|
||||
import akka.routing._
|
||||
import akka.util.Timeout
|
||||
import akka.AkkaException
|
||||
import akka.util.{ Duration, Switch, Helpers }
|
||||
import akka.event._
|
||||
|
|
@ -352,6 +356,9 @@ class LocalActorRefProvider(
|
|||
case StopChild(child) ⇒ context.stop(child); sender ! "ok"
|
||||
case m ⇒ deadLetters ! DeadLetter(m, sender, self)
|
||||
}
|
||||
|
||||
// guardian MUST NOT lose its children during restart
|
||||
override def preRestart(cause: Throwable, msg: Option[Any]) {}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -369,6 +376,9 @@ class LocalActorRefProvider(
|
|||
case StopChild(child) ⇒ context.stop(child); sender ! "ok"
|
||||
case m ⇒ deadLetters ! DeadLetter(m, sender, self)
|
||||
}
|
||||
|
||||
// guardian MUST NOT lose its children during restart
|
||||
override def preRestart(cause: Throwable, msg: Option[Any]) {}
|
||||
}
|
||||
|
||||
private val guardianFaultHandlingStrategy = {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import akka.actor._
|
|||
import akka.event._
|
||||
import akka.dispatch._
|
||||
import akka.util.duration._
|
||||
import akka.util.Timeout
|
||||
import akka.util.Timeout._
|
||||
import org.jboss.netty.akka.util.HashedWheelTimer
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import java.util.concurrent.TimeUnit.NANOSECONDS
|
||||
|
|
@ -97,9 +99,6 @@ object ActorSystem {
|
|||
case "" ⇒ None
|
||||
case x ⇒ Some(x)
|
||||
}
|
||||
val BootClasses: Seq[String] = getStringList("akka.boot").asScala
|
||||
|
||||
val EnabledModules: Seq[String] = getStringList("akka.enabled-modules").asScala
|
||||
|
||||
val SchedulerTickDuration = Duration(getMilliseconds("akka.scheduler.tickDuration"), MILLISECONDS)
|
||||
val SchedulerTicksPerWheel = getInt("akka.scheduler.ticksPerWheel")
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.actor
|
||||
|
||||
import java.io.File
|
||||
import java.net.{ URL, URLClassLoader }
|
||||
import java.util.jar.JarFile
|
||||
import akka.util.Bootable
|
||||
|
||||
/**
|
||||
* Handles all modules in the deploy directory (load and unload)
|
||||
*/
|
||||
trait BootableActorLoaderService extends Bootable {
|
||||
|
||||
def system: ActorSystem
|
||||
|
||||
val BOOT_CLASSES = system.settings.BootClasses
|
||||
lazy val applicationLoader = createApplicationClassLoader()
|
||||
|
||||
protected def createApplicationClassLoader(): Option[ClassLoader] = Some({
|
||||
if (system.settings.Home.isDefined) {
|
||||
val DEPLOY = system.settings.Home.get + "/deploy"
|
||||
val DEPLOY_DIR = new File(DEPLOY)
|
||||
if (!DEPLOY_DIR.exists) {
|
||||
System.exit(-1)
|
||||
}
|
||||
val filesToDeploy = DEPLOY_DIR.listFiles.toArray.toList
|
||||
.asInstanceOf[List[File]].filter(_.getName.endsWith(".jar"))
|
||||
var dependencyJars: List[URL] = Nil
|
||||
filesToDeploy.map { file ⇒
|
||||
val jarFile = new JarFile(file)
|
||||
val en = jarFile.entries
|
||||
while (en.hasMoreElements) {
|
||||
val name = en.nextElement.getName
|
||||
if (name.endsWith(".jar")) dependencyJars ::= new File(
|
||||
String.format("jar:file:%s!/%s", jarFile.getName, name)).toURI.toURL
|
||||
}
|
||||
}
|
||||
val toDeploy = filesToDeploy.map(_.toURI.toURL)
|
||||
val allJars = toDeploy ::: dependencyJars
|
||||
|
||||
new URLClassLoader(allJars.toArray, Thread.currentThread.getContextClassLoader)
|
||||
} else Thread.currentThread.getContextClassLoader
|
||||
})
|
||||
|
||||
abstract override def onLoad() = {
|
||||
super.onLoad()
|
||||
|
||||
applicationLoader foreach Thread.currentThread.setContextClassLoader
|
||||
|
||||
for (loader ← applicationLoader; clazz ← BOOT_CLASSES) {
|
||||
loader.loadClass(clazz).newInstance
|
||||
}
|
||||
}
|
||||
|
||||
abstract override def onUnload() = {
|
||||
super.onUnload()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API for the default JAX-RS/Mist Initializer
|
||||
*/
|
||||
class DefaultBootableActorLoaderService(val system: ActorSystem) extends BootableActorLoaderService
|
||||
|
|
@ -46,15 +46,15 @@ object IO {
|
|||
override def asReadable = this
|
||||
|
||||
def read(len: Int)(implicit actor: Actor with IO): ByteString @cps[IOSuspendable[Any]] = shift { cont: (ByteString ⇒ IOSuspendable[Any]) ⇒
|
||||
ByteStringLength(cont, this, actor.context.currentMessage, len)
|
||||
ByteStringLength(cont, this, actor.context.asInstanceOf[ActorCell].currentMessage, len)
|
||||
}
|
||||
|
||||
def read()(implicit actor: Actor with IO): ByteString @cps[IOSuspendable[Any]] = shift { cont: (ByteString ⇒ IOSuspendable[Any]) ⇒
|
||||
ByteStringAny(cont, this, actor.context.currentMessage)
|
||||
ByteStringAny(cont, this, actor.context.asInstanceOf[ActorCell].currentMessage)
|
||||
}
|
||||
|
||||
def read(delimiter: ByteString, inclusive: Boolean = false)(implicit actor: Actor with IO): ByteString @cps[IOSuspendable[Any]] = shift { cont: (ByteString ⇒ IOSuspendable[Any]) ⇒
|
||||
ByteStringDelimited(cont, this, actor.context.currentMessage, delimiter, inclusive, 0)
|
||||
ByteStringDelimited(cont, this, actor.context.asInstanceOf[ActorCell].currentMessage, delimiter, inclusive, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ trait IO {
|
|||
}
|
||||
run()
|
||||
case msg if _next ne Idle ⇒
|
||||
_messages enqueue context.currentMessage
|
||||
_messages enqueue context.asInstanceOf[ActorCell].currentMessage
|
||||
case msg if _receiveIO.isDefinedAt(msg) ⇒
|
||||
_next = reset { _receiveIO(msg); Idle }
|
||||
run()
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ import akka.dispatch._
|
|||
import akka.japi.Creator
|
||||
import akka.util._
|
||||
import collection.immutable.Stack
|
||||
import akka.routing.{ NoRouter, RouterConfig }
|
||||
import akka.routing._
|
||||
|
||||
/**
|
||||
* Factory for Props instances.
|
||||
*
|
||||
* Props is a ActorRef configuration object, that is thread safe and fully sharable.
|
||||
*
|
||||
* Used when creating new actors through; <code>ActorSystem.actorOf</code> and <code>ActorContext.actorOf</code>.
|
||||
*/
|
||||
object Props {
|
||||
|
|
@ -47,6 +49,8 @@ object Props {
|
|||
/**
|
||||
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
|
||||
* of the supplied type using the default constructor.
|
||||
*
|
||||
* Scala API.
|
||||
*/
|
||||
def apply[T <: Actor: ClassManifest]: Props =
|
||||
default.withCreator(implicitly[ClassManifest[T]].erasure.asInstanceOf[Class[_ <: Actor]].newInstance)
|
||||
|
|
@ -61,6 +65,8 @@ object Props {
|
|||
/**
|
||||
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
|
||||
* using the supplied thunk.
|
||||
*
|
||||
* Scala API.
|
||||
*/
|
||||
def apply(creator: ⇒ Actor): Props =
|
||||
default.withCreator(creator)
|
||||
|
|
@ -87,7 +93,16 @@ object Props {
|
|||
* {{{
|
||||
* val props = Props[MyActor]
|
||||
* val props = Props(new MyActor)
|
||||
* val props = Props(
|
||||
* creator = ..,
|
||||
* dispatcher = ..,
|
||||
* timeout = ..,
|
||||
* faultHandler = ..,
|
||||
* routerConfig = ..
|
||||
* )
|
||||
* val props = Props().withCreator(new MyActor)
|
||||
* val props = Props[MyActor].withTimeout(timeout)
|
||||
* val props = Props[MyActor].withRouter(RoundRobinRouter(..))
|
||||
* val props = Props[MyActor].withFaultHandler(OneForOneStrategy {
|
||||
* case e: IllegalStateException ⇒ Resume
|
||||
* })
|
||||
|
|
@ -103,11 +118,13 @@ object Props {
|
|||
* }
|
||||
* });
|
||||
* Props props = new Props().withCreator(new UntypedActorFactory() { ... });
|
||||
* Props props = new Props().withTimeout(timeout);
|
||||
* Props props = new Props().withFaultHandler(new OneForOneStrategy(...));
|
||||
* Props props = new Props(MyActor.class).withTimeout(timeout);
|
||||
* Props props = new Props(MyActor.class).withFaultHandler(new OneForOneStrategy(...));
|
||||
* Props props = new Props(MyActor.class).withRouter(new RoundRobinRouter(..));
|
||||
* }}}
|
||||
*/
|
||||
case class Props(creator: () ⇒ Actor = Props.defaultCreator,
|
||||
case class Props(
|
||||
creator: () ⇒ Actor = Props.defaultCreator,
|
||||
@transient dispatcher: MessageDispatcher = Props.defaultDispatcher,
|
||||
timeout: Timeout = Props.defaultTimeout,
|
||||
faultHandler: FaultHandlingStrategy = Props.defaultFaultHandler,
|
||||
|
|
@ -115,7 +132,6 @@ case class Props(creator: () ⇒ Actor = Props.defaultCreator,
|
|||
|
||||
/**
|
||||
* No-args constructor that sets all the default values.
|
||||
* Java API.
|
||||
*/
|
||||
def this() = this(
|
||||
creator = Props.defaultCreator,
|
||||
|
|
@ -144,43 +160,42 @@ case class Props(creator: () ⇒ Actor = Props.defaultCreator,
|
|||
|
||||
/**
|
||||
* Returns a new Props with the specified creator set.
|
||||
*
|
||||
* Scala API.
|
||||
*/
|
||||
def withCreator(c: ⇒ Actor) = copy(creator = () ⇒ c)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified creator set.
|
||||
*
|
||||
* Java API.
|
||||
*/
|
||||
def withCreator(c: Creator[Actor]) = copy(creator = () ⇒ c.create)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified creator set.
|
||||
*
|
||||
* Java API.
|
||||
*/
|
||||
def withCreator(c: Class[_ <: Actor]) = copy(creator = () ⇒ c.newInstance)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified dispatcher set.
|
||||
* Java API.
|
||||
*/
|
||||
def withDispatcher(d: MessageDispatcher) = copy(dispatcher = d)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified timeout set
|
||||
* Java API.
|
||||
* Returns a new Props with the specified timeout set.
|
||||
*/
|
||||
def withTimeout(t: Timeout) = copy(timeout = t)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified faulthandler set.
|
||||
* Java API.
|
||||
*/
|
||||
def withFaultHandler(f: FaultHandlingStrategy) = copy(faultHandler = f)
|
||||
|
||||
/**
|
||||
* Returns a new Props with the specified router config set
|
||||
* Java API
|
||||
* Returns a new Props with the specified router config set.
|
||||
*/
|
||||
def withRouter(r: RouterConfig) = copy(routerConfig = r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
|||
|
||||
import akka.japi.{ Creator, Option ⇒ JOption }
|
||||
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
|
||||
import akka.util.{ Duration }
|
||||
import akka.util.{ Duration, Timeout }
|
||||
import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar }
|
||||
import akka.serialization.{ Serializer, Serialization }
|
||||
import akka.dispatch._
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package akka.dispatch
|
|||
|
||||
import akka.AkkaException
|
||||
import akka.event.Logging.Error
|
||||
import akka.actor.Timeout
|
||||
import akka.util.Timeout
|
||||
import scala.Option
|
||||
import akka.japi.{ Procedure, Function ⇒ JFunc, Option ⇒ JOption }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package akka.dispatch
|
|||
import java.util.concurrent.atomic.AtomicReference
|
||||
import scala.util.continuations._
|
||||
import scala.annotation.{ tailrec }
|
||||
import akka.actor.Timeout
|
||||
import akka.util.Timeout
|
||||
|
||||
object PromiseStream {
|
||||
def apply[A]()(implicit dispatcher: MessageDispatcher, timeout: Timeout): PromiseStream[A] = new PromiseStream[A]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package akka.dispatch.japi
|
||||
|
||||
import akka.actor.Timeout
|
||||
import akka.util.Timeout
|
||||
import akka.japi.{ Procedure2, Procedure, Function ⇒ JFunc, Option ⇒ JOption }
|
||||
|
||||
/* Java API */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import akka.util.ReflectiveAccess
|
|||
import akka.config.ConfigurationException
|
||||
import akka.util.ReentrantGuard
|
||||
import akka.util.duration._
|
||||
import akka.actor.Timeout
|
||||
import akka.util.Timeout
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.actor.ActorRefProvider
|
||||
import scala.util.control.NoStackTrace
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@
|
|||
package akka.routing
|
||||
|
||||
import akka.actor._
|
||||
|
||||
import akka.japi.Creator
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import akka.config.ConfigurationException
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.util.ReflectiveAccess
|
||||
import akka.util.{ ReflectiveAccess, Timeout }
|
||||
import akka.AkkaException
|
||||
import scala.collection.JavaConversions._
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
|
|||
|
|
@ -5,7 +5,5 @@
|
|||
package akka
|
||||
|
||||
package object routing {
|
||||
|
||||
type Route = PartialFunction[(akka.actor.ActorRef, Any), Iterable[Destination]]
|
||||
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
import akka.actor.ActorSystem
|
||||
|
||||
/*
|
||||
* This class is responsible for booting up a stack of bundles and then shutting them down
|
||||
*/
|
||||
class AkkaLoader(system: ActorSystem) {
|
||||
private val hasBooted = new Switch(false)
|
||||
|
||||
@volatile
|
||||
private var _bundles: Option[Bootable] = None
|
||||
|
||||
def bundles = _bundles;
|
||||
|
||||
/*
|
||||
* Boot initializes the specified bundles
|
||||
*/
|
||||
def boot(withBanner: Boolean, b: Bootable): Unit = hasBooted switchOn {
|
||||
if (withBanner) printBanner()
|
||||
println("Starting Akka...")
|
||||
b.onLoad()
|
||||
Thread.currentThread.setContextClassLoader(getClass.getClassLoader)
|
||||
_bundles = Some(b)
|
||||
println("Akka started successfully")
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown, well, shuts down the bundles used in boot
|
||||
*/
|
||||
def shutdown() {
|
||||
hasBooted switchOff {
|
||||
println("Shutting down Akka...")
|
||||
_bundles.foreach(_.onUnload())
|
||||
_bundles = None
|
||||
println("Akka succesfully shut down")
|
||||
}
|
||||
}
|
||||
|
||||
private def printBanner() {
|
||||
println("""
|
||||
==============================================================================
|
||||
|
||||
ZZ:
|
||||
ZZZZ
|
||||
ZZZZZZ
|
||||
ZZZ' ZZZ
|
||||
~7 7ZZ' ZZZ
|
||||
:ZZZ: IZZ' ZZZ
|
||||
,OZZZZ.~ZZ? ZZZ
|
||||
ZZZZ' 'ZZZ$ ZZZ
|
||||
. $ZZZ ~ZZ$ ZZZ
|
||||
.=Z?. .ZZZO ~ZZ7 OZZ
|
||||
.ZZZZ7..:ZZZ~ 7ZZZ ZZZ~
|
||||
.$ZZZ$Z+.ZZZZ ZZZ: ZZZ$
|
||||
.,ZZZZ?' =ZZO= .OZZ 'ZZZ
|
||||
.$ZZZZ+ .ZZZZ IZZZ ZZZ$
|
||||
.ZZZZZ' .ZZZZ' .ZZZ$ ?ZZZ
|
||||
.ZZZZZZ' .OZZZ? ?ZZZ 'ZZZ$
|
||||
.?ZZZZZZ' .ZZZZ? .ZZZ? 'ZZZO
|
||||
.+ZZZZZZ?' .7ZZZZ' .ZZZZ :ZZZZ
|
||||
.ZZZZZZ$' .?ZZZZZ' .~ZZZZ 'ZZZZ.
|
||||
|
||||
|
||||
NNNNN $NNNN+
|
||||
NNNNN $NNNN+
|
||||
NNNNN $NNNN+
|
||||
NNNNN $NNNN+
|
||||
NNNNN $NNNN+
|
||||
=NNNNNNNNND$ NNNNN DDDDDD: $NNNN+ DDDDDN NDDNNNNNNNN,
|
||||
NNNNNNNNNNNNND NNNNN DNNNNN $NNNN+ 8NNNNN= :NNNNNNNNNNNNNN
|
||||
NNNNN$ DNNNNN NNNNN $NNNNN~ $NNNN+ NNNNNN NNNNN, :NNNNN+
|
||||
?DN~ NNNNN NNNNN MNNNNN $NNNN+:NNNNN7 $ND =NNNNN
|
||||
DNNNNN NNNNNDNNNN$ $NNNNDNNNNN :DNNNNN
|
||||
ZNDNNNNNNNNND NNNNNNNNNND, $NNNNNNNNNNN DNDNNNNNNNNNN
|
||||
NNNNNNNDDINNNNN NNNNNNNNNNND $NNNNNNNNNNND ONNNNNNND8+NNNNN
|
||||
:NNNND NNNNN NNNNNN DNNNN, $NNNNNO 7NNNND NNNNNO :NNNNN
|
||||
DNNNN NNNNN NNNNN DNNNN $NNNN+ 8NNNNN NNNNN $NNNNN
|
||||
DNNNNO NNNNNN NNNNN NNNNN $NNNN+ NNNNN$ NNNND, ,NNNNND
|
||||
NNNNNNDDNNNNNNNN NNNNN =NNNNN $NNNN+ DNNNN? DNNNNNNDNNNNNNNND
|
||||
NNNNNNNNN NNNN$ NNNNN 8NNNND $NNNN+ NNNNN= ,DNNNNNNND NNNNN$
|
||||
|
||||
==============================================================================
|
||||
Running version %s
|
||||
==============================================================================
|
||||
""".format(ActorSystem.Version))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
import akka.actor.ActorSystem
|
||||
|
||||
trait Bootable {
|
||||
def onLoad() {}
|
||||
def onUnload() {}
|
||||
}
|
||||
|
|
@ -148,8 +148,6 @@ object Duration {
|
|||
trait Infinite {
|
||||
this: Duration ⇒
|
||||
|
||||
override def equals(other: Any) = false
|
||||
|
||||
def +(other: Duration): Duration =
|
||||
other match {
|
||||
case _: this.type ⇒ this
|
||||
|
|
@ -192,7 +190,7 @@ object Duration {
|
|||
*/
|
||||
val Inf: Duration = new Duration with Infinite {
|
||||
override def toString = "Duration.Inf"
|
||||
def compare(other: Duration) = 1
|
||||
def compare(other: Duration) = if (other eq this) 0 else 1
|
||||
def unary_- : Duration = MinusInf
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +200,7 @@ object Duration {
|
|||
*/
|
||||
val MinusInf: Duration = new Duration with Infinite {
|
||||
override def toString = "Duration.MinusInf"
|
||||
def compare(other: Duration) = -1
|
||||
def compare(other: Duration) = if (other eq this) 0 else -1
|
||||
def unary_- : Duration = Inf
|
||||
}
|
||||
|
||||
|
|
@ -545,3 +543,29 @@ class DurationDouble(d: Double) {
|
|||
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS))
|
||||
}
|
||||
|
||||
case class Timeout(duration: Duration) {
|
||||
def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
|
||||
def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
|
||||
}
|
||||
|
||||
object Timeout {
|
||||
/**
|
||||
* A timeout with zero duration, will cause most requests to always timeout.
|
||||
*/
|
||||
val zero = new Timeout(Duration.Zero)
|
||||
|
||||
/**
|
||||
* A Timeout with infinite duration. Will never timeout. Use extreme caution with this
|
||||
* as it may cause memory leaks, blocked threads, or may not even be supported by
|
||||
* the receiver, which would result in an exception.
|
||||
*/
|
||||
val never = new Timeout(Duration.Inf)
|
||||
|
||||
def apply(timeout: Long) = new Timeout(timeout)
|
||||
def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit)
|
||||
|
||||
implicit def durationToTimeout(duration: Duration) = new Timeout(duration)
|
||||
implicit def intToTimeout(timeout: Int) = new Timeout(timeout)
|
||||
implicit def longToTimeout(timeout: Long) = new Timeout(timeout)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
|
||||
import scala.util.continuations._
|
||||
import akka.dispatch.MessageDispatcher
|
||||
import akka.actor.Timeout
|
||||
|
||||
package object cps {
|
||||
def matchC[A, B, C, D](in: A)(pf: PartialFunction[A, B @cpsParam[C, D]]): B @cpsParam[C, D] = pf(in)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package akka.camel;
|
|||
import akka.actor.Actor;
|
||||
import akka.actor.TypedActor;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Timeout;
|
||||
import akka.util.Timeout;
|
||||
import akka.dispatch.Dispatchers;
|
||||
import akka.japi.SideEffect;
|
||||
import akka.util.FiniteDuration;
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@ Common utilities
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
scheduler
|
||||
duration
|
||||
|
|
|
|||
|
|
@ -84,14 +84,6 @@ 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
|
||||
|
||||
|
||||
sbt interactive mode
|
||||
--------------------
|
||||
|
||||
|
|
@ -129,6 +121,6 @@ Dependencies
|
|||
You can look at the Ivy dependency resolution information that is created on
|
||||
``sbt update`` and found in ``~/.ivy2/cache``. For example, the
|
||||
``~/.ivy2/cache/com.typesafe.akka-akka-remote-compile.xml`` file contains
|
||||
the resolution information for the akka-cluster module compile dependencies. If
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
|
||||
.. _team:
|
||||
|
||||
######
|
||||
Team
|
||||
=====
|
||||
######
|
||||
|
||||
=================== ========================== ====================================
|
||||
Name Role Email
|
||||
|
|
|
|||
147
akka-docs/disabled/agents.rst
Normal file
147
akka-docs/disabled/agents.rst
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
Agents (Scala)
|
||||
==============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Agents in Akka were inspired by `agents in Clojure <http://clojure.org/agents>`_.
|
||||
|
||||
Agents provide asynchronous change of individual locations. Agents are bound to a single storage location for their lifetime, and only allow mutation of that location (to a new state) to occur as a result of an action. Update actions are functions that are asynchronously applied to the Agent's state and whose return value becomes the Agent's new state. The state of an Agent should be immutable.
|
||||
|
||||
While updates to Agents are asynchronous, the state of an Agent is always immediately available for reading by any thread (using ``get`` or ``apply``) without any messages.
|
||||
|
||||
Agents are reactive. The update actions of all Agents get interleaved amongst threads in a thread pool. At any point in time, at most one ``send`` action for each Agent is being executed. Actions dispatched to an agent from another thread will occur in the order they were sent, potentially interleaved with actions dispatched to the same agent from other sources.
|
||||
|
||||
If an Agent is used within an enclosing transaction, then it will participate in that transaction. 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.
|
||||
|
||||
Creating and stopping Agents
|
||||
----------------------------
|
||||
|
||||
Agents are created by invoking ``Agent(value)`` passing in the Agent's initial value.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val agent = Agent(5)
|
||||
|
||||
An Agent will be running until you invoke ``close`` on it. Then it will be eligible for garbage collection (unless you hold on to it in some way).
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
agent.close()
|
||||
|
||||
Updating Agents
|
||||
---------------
|
||||
|
||||
You update an Agent by sending a function that transforms the current value or by sending just a new value. The Agent will apply the new value or function atomically and asynchronously. The update is done in a fire-forget manner and you are only guaranteed that it will be applied. There is no guarantee of when the update will be applied but dispatches to an Agent from a single thread will occur in order. You apply a value or a function by invoking the ``send`` function.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
// send a value
|
||||
agent send 7
|
||||
|
||||
// send a function
|
||||
agent send (_ + 1)
|
||||
agent send (_ * 2)
|
||||
|
||||
You can also dispatch a function to update the internal state but on its own thread. This does not use the reactive thread pool and can be used for long-running or blocking operations. You do this with the ``sendOff`` method. Dispatches using either ``sendOff`` or ``send`` will still be executed in order.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
// sendOff a function
|
||||
agent sendOff (longRunningOrBlockingFunction)
|
||||
|
||||
Reading an Agent's value
|
||||
------------------------
|
||||
|
||||
Agents can be dereferenced, e.g. you can get an Agent's value, by invoking the Agent with parenthesis like this:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent()
|
||||
|
||||
Or by using the get method.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent.get
|
||||
|
||||
Reading an Agent's current value does not involve any message passing and happens immediately. So while updates to an Agent are asynchronous, reading the state of an Agent is synchronous.
|
||||
|
||||
Awaiting an Agent's value
|
||||
-------------------------
|
||||
|
||||
It is also possible to read the value after all currently queued ``send``\s have completed. You can do this with ``await``:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent.await
|
||||
|
||||
You can also get a ``Future`` to this value, that will be completed after the currently queued updates have completed:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val future = agent.future
|
||||
// ...
|
||||
val result = future.await.result.get
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
Agents are also monadic, allowing you to compose operations using for-comprehensions. In a monadic usage, new Agents are created leaving the original Agents untouched. So the old values (Agents) are still available as-is. They are so-called 'persistent'.
|
||||
|
||||
Example of a monadic usage:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val agent1 = Agent(3)
|
||||
val agent2 = Agent(5)
|
||||
|
||||
// uses foreach
|
||||
var result = 0
|
||||
for (value <- agent1) {
|
||||
result = value + 1
|
||||
}
|
||||
|
||||
// uses map
|
||||
val agent3 =
|
||||
for (value <- agent1) yield value + 1
|
||||
|
||||
// uses flatMap
|
||||
val agent4 = for {
|
||||
value1 <- agent1
|
||||
value2 <- agent2
|
||||
} yield value1 + value2
|
||||
|
||||
agent1.close()
|
||||
agent2.close()
|
||||
agent3.close()
|
||||
agent4.close()
|
||||
|
|
@ -103,30 +103,30 @@ A custom ``application.conf`` might look like this::
|
|||
# Copy in parts of the reference files and modify as you please.
|
||||
|
||||
akka {
|
||||
|
||||
# Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT)
|
||||
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
|
||||
loglevel = DEBUG # Options: ERROR, WARNING, INFO, DEBUG
|
||||
# this level is used by the configured loggers (see "event-handlers") as soon
|
||||
|
||||
# Log level used by the configured loggers (see "event-handlers") as soon
|
||||
# as they have been started; before that, see "stdout-loglevel"
|
||||
stdout-loglevel = DEBUG # Loglevel for the very basic logger activated during AkkaApplication startup
|
||||
# Options: ERROR, WARNING, INFO, DEBUG
|
||||
loglevel = DEBUG
|
||||
|
||||
# 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"]
|
||||
# Log level for the very basic logger activated during AkkaApplication startup
|
||||
# Options: ERROR, WARNING, INFO, DEBUG
|
||||
stdout-loglevel = DEBUG
|
||||
|
||||
actor {
|
||||
default-dispatcher {
|
||||
throughput = 10 # Throughput for default Dispatcher, set to 1 for as fair as possible
|
||||
# Throughput for default Dispatcher, set to 1 for as fair as possible
|
||||
throughput = 10
|
||||
}
|
||||
}
|
||||
|
||||
remote {
|
||||
server {
|
||||
port = 2562 # The port clients should connect to. Default is 2552 (AKKA)
|
||||
# The port clients should connect to. Default is 2552 (AKKA)
|
||||
port = 2562
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
How can I use and deploy Akka?
|
||||
==============================
|
||||
|
||||
Akka can be used in two different ways:
|
||||
Akka can be used in different ways:
|
||||
|
||||
- As a library: used as a regular JAR on the classpath and/or in a web app, to
|
||||
be put into ``WEB-INF/lib``
|
||||
|
||||
- As a microkernel: stand-alone microkernel, embedding a servlet container along
|
||||
with many other services
|
||||
- As a stand alone application by instantiating ActorSystem in a main class or
|
||||
using the :ref:`microkernel`
|
||||
|
||||
|
||||
Using Akka as library
|
||||
|
|
@ -38,48 +38,7 @@ on other hosts. Please note that remoting service does not speak HTTP over port
|
|||
|
||||
|
||||
Using Akka as a stand alone microkernel
|
||||
---------------------------------------
|
||||
----------------------------------------
|
||||
|
||||
Akka can also be run as a stand-alone microkernel. It implements a full
|
||||
enterprise stack. See the :ref:`microkernel` for more information.
|
||||
|
||||
Using the Akka sbt plugin to package your application
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The Akka sbt plugin can create a full Akka microkernel deployment for your sbt
|
||||
project.
|
||||
|
||||
To use the plugin, first add a plugin definition to your sbt project by creating
|
||||
``project/plugins.sbt`` with::
|
||||
|
||||
resolvers += Classpaths.typesafeResolver
|
||||
|
||||
addSbtPlugin("com.typesafe.akka" % "akka-sbt-plugin" % "2.0-SNAPSHOT")
|
||||
|
||||
Then use the AkkaKernelPlugin settings. In a 'light' configuration (build.sbt)::
|
||||
|
||||
seq(akka.sbt.AkkaKernelPlugin.distSettings: _*)
|
||||
|
||||
Or in a 'full' configuration (Build.scala). For example::
|
||||
|
||||
import sbt._
|
||||
import sbt.Keys._
|
||||
import akka.sbt.AkkaKernelPlugin
|
||||
|
||||
object SomeBuild extends Build {
|
||||
lazy val someProject = Project(
|
||||
id = "some-project",
|
||||
base = file("."),
|
||||
settings = Defaults.defaultSettings ++ AkkaKernelPlugin.distSettings ++ Seq(
|
||||
organization := "org.some",
|
||||
version := "0.1",
|
||||
scalaVersion := "2.9.1"
|
||||
resolvers += "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases/",
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-kernel" % "2.0-SNAPSHOT"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
To build a microkernel deployment use the ``dist`` task::
|
||||
|
||||
sbt dist
|
||||
Akka can also be run as a stand-alone microkernel. See :ref:`microkernel` for
|
||||
more information.
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ Downloading and installing Akka
|
|||
-------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to download
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skip
|
||||
this section and jump to the next one.
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skip this
|
||||
section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
Let's get the ``akka-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -77,46 +77,55 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ cd /Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
/Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
$ ls -1
|
||||
bin
|
||||
config
|
||||
deploy
|
||||
doc
|
||||
lib
|
||||
src
|
||||
|
||||
- In the ``bin`` directory we have scripts for starting the Akka Microkernel.
|
||||
- In the ``config`` directory we have the Akka conf files.
|
||||
- In the ``doc`` directory we have the documentation, API, doc JARs, and also
|
||||
the source files for the tutorials.
|
||||
- In the ``deploy`` directory we can place applications to be run with the microkernel.
|
||||
- In the ``doc`` directory we have the documentation, API, and doc JARs.
|
||||
- In the ``lib`` directory we have the Scala and Akka JARs.
|
||||
- In the ``src`` directory we have the source JARs for Akka.
|
||||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
Akka is very modular and has many JARs for containing different features. The
|
||||
modules are:
|
||||
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor`` -- Actors
|
||||
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
- ``akka-remote`` -- Remote Actors
|
||||
|
||||
- ``akka-slf4j`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
|
||||
- ``akka-testkit`` -- Toolkit for testing Actors
|
||||
|
||||
- ``akka-kernel`` -- Akka microkernel for running a bare-bones mini application server
|
||||
|
||||
- ``akka-durable-mailboxes`` -- Durable mailboxes: file-based, MongoDB, Redis, Zookeeper
|
||||
|
||||
- ``akka-amqp`` -- AMQP integration
|
||||
|
||||
.. - ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
.. - ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
.. - ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
.. - ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing Maven
|
||||
|
|
@ -158,16 +167,19 @@ Here is the layout that Maven created::
|
|||
|
||||
As you can see we already have a Java source file called ``App.java``, let's now rename it to ``Pi.java``.
|
||||
|
||||
We also need to edit the ``pom.xml`` build file. Let's add the dependency we need as well as the Maven repository it should download it from. The Akka Maven repository can be found at `<http://akka.io/repository>`_
|
||||
and Typesafe provides `<http://repo.typesafe.com/typesafe/releases/>`_ that proxies several other repositories, including akka.io.
|
||||
It should now look something like this:
|
||||
We also need to edit the ``pom.xml`` build file. Let's add the dependency we
|
||||
need as well as the Maven repository it should download it from. The Akka Maven
|
||||
repository can be found at http://akka.io/releases/ and Typesafe provides
|
||||
http://repo.typesafe.com/typesafe/releases/ that proxies several other
|
||||
repositories, including akka.io. It should now look something like this:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<name>akka-tutorial-first-java</name>
|
||||
|
|
@ -213,28 +225,15 @@ Start writing the code
|
|||
|
||||
Now it's about time to start hacking.
|
||||
|
||||
We start by creating a ``Pi.java`` file and adding these import statements at the top of the file::
|
||||
We start by creating a ``Pi.java`` file and adding these import statements at the top of the file:
|
||||
|
||||
package akka.tutorial.first.java;
|
||||
|
||||
import static akka.actor.Actors.actorOf;
|
||||
import static akka.actor.Actors.poisonPill;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import akka.actor.Props;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.routing.CyclicIterator;
|
||||
import akka.routing.InfiniteIterator;
|
||||
import akka.routing.Routing.Broadcast;
|
||||
import akka.routing.UntypedLoadBalancer;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#imports
|
||||
|
||||
If you are using Maven in this tutorial then create the file in the ``src/main/java/akka/tutorial/first/java`` directory.
|
||||
|
||||
If you are using the command line tools then create the file wherever you want. I will create it in a directory called ``tutorial`` at the root of the Akka distribution, e.g. in ``$AKKA_HOME/tutorial/akka/tutorial/first/java/Pi.java``.
|
||||
If you are using the command line tools then create the file wherever you want.
|
||||
We will create it in a directory called ``tutorial`` at the root of the Akka distribution,
|
||||
e.g. in ``$AKKA_HOME/tutorial/akka/tutorial/first/java/Pi.java``.
|
||||
|
||||
Creating the messages
|
||||
---------------------
|
||||
|
|
@ -247,466 +246,101 @@ With this in mind, let's now create the messages that we want to have flowing in
|
|||
- ``Work`` -- sent from the ``Master`` actor to the ``Worker`` actors containing the work assignment
|
||||
- ``Result`` -- sent from the ``Worker`` actors to the ``Master`` actor containing the result from the worker's calculation
|
||||
|
||||
Messages sent to actors should always be immutable to avoid sharing mutable state. So let's start by creating three messages as immutable POJOs. We also create a wrapper ``Pi`` class to hold our implementation::
|
||||
Messages sent to actors should always be immutable to avoid sharing mutable state. So let's start by creating three messages as immutable POJOs. We also create a wrapper ``Pi`` class to hold our implementation:
|
||||
|
||||
public class Pi {
|
||||
|
||||
static class Calculate {}
|
||||
|
||||
static class Work {
|
||||
private final int start;
|
||||
private final int nrOfElements;
|
||||
|
||||
public Work(int start, int nrOfElements) {
|
||||
this.start = start;
|
||||
this.nrOfElements = nrOfElements;
|
||||
}
|
||||
|
||||
public int getStart() { return start; }
|
||||
public int getNrOfElements() { return nrOfElements; }
|
||||
}
|
||||
|
||||
static class Result {
|
||||
private final double value;
|
||||
|
||||
public Result(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public double getValue() { return value; }
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#messages
|
||||
|
||||
Creating the worker
|
||||
-------------------
|
||||
|
||||
Now we can create the worker actor. This is done by extending in the ``UntypedActor`` base class and defining the ``onReceive`` method. The ``onReceive`` method defines our message handler. We expect it to be able to handle the ``Work`` message so we need to add a handler for this message::
|
||||
Now we can create the worker actor. This is done by extending in the ``UntypedActor`` base class and defining the ``onReceive`` method. The ``onReceive`` method defines our message handler. We expect it to be able to handle the ``Work`` message so we need to add a handler for this message:
|
||||
|
||||
static class Worker extends UntypedActor {
|
||||
|
||||
// message handler
|
||||
public void onReceive(Object message) {
|
||||
if (message instanceof Work) {
|
||||
Work work = (Work) message;
|
||||
|
||||
// perform the work
|
||||
double result = calculatePiFor(work.getStart(), work.getNrOfElements());
|
||||
|
||||
// reply with the result
|
||||
getContext().reply(new Result(result));
|
||||
|
||||
} else throw new IllegalArgumentException("Unknown message [" + message + "]");
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#worker
|
||||
:exclude: calculatePiFor
|
||||
|
||||
As you can see we have now created an ``UntypedActor`` with a ``onReceive`` method as a handler for the ``Work`` message. In this handler we invoke the ``calculatePiFor(..)`` method, wrap the result in a ``Result`` message and send it back to the original sender using ``getContext().reply(..)``. In Akka the sender reference is implicitly passed along with the message so that the receiver can always reply or store away the sender reference for future use.
|
||||
|
||||
The only thing missing in our ``Worker`` actor is the implementation on the ``calculatePiFor(..)`` method::
|
||||
The only thing missing in our ``Worker`` actor is the implementation on the ``calculatePiFor(..)`` method:
|
||||
|
||||
// define the work
|
||||
private double calculatePiFor(int start, int nrOfElements) {
|
||||
double acc = 0.0;
|
||||
for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) {
|
||||
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#calculatePiFor
|
||||
|
||||
Creating the master
|
||||
-------------------
|
||||
|
||||
The master actor is a little bit more involved. In its constructor we need to create the workers (the ``Worker`` actors) and start them. We will also wrap them in a load-balancing router to make it easier to spread out the work evenly between the workers. Let's do that first::
|
||||
The master actor is a little bit more involved. In its constructor we create a round-robin router
|
||||
to make it easier to spread out the work evenly between the workers. Let's do that first:
|
||||
|
||||
static class Master extends UntypedActor {
|
||||
...
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#create-router
|
||||
|
||||
static class PiRouter extends UntypedLoadBalancer {
|
||||
private final InfiniteIterator<ActorRef> workers;
|
||||
|
||||
public PiRouter(ActorRef[] workers) {
|
||||
this.workers = new CyclicIterator<ActorRef>(asList(workers));
|
||||
}
|
||||
|
||||
public InfiniteIterator<ActorRef> seq() {
|
||||
return workers;
|
||||
}
|
||||
}
|
||||
|
||||
public Master(...) {
|
||||
...
|
||||
|
||||
// create the workers
|
||||
final ActorRef[] workers = new ActorRef[nrOfWorkers];
|
||||
for (int i = 0; i < nrOfWorkers; i++) {
|
||||
workers[i] = actorOf(new Props(Worker.class));
|
||||
}
|
||||
|
||||
// wrap them with a load-balancing router
|
||||
ActorRef router = actorOf(new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new PiRouter(workers);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
As you can see we are using the ``actorOf`` factory method to create actors, this method returns as an ``ActorRef`` which is a reference to our newly created actor. This method is available in the ``Actors`` object but is usually imported::
|
||||
|
||||
import static akka.actor.Actors.actorOf;
|
||||
|
||||
One thing to note is that we used two different versions of the ``actorOf`` method. For creating the ``Worker`` actor we just pass in the class but to create the ``PiRouter`` actor we can't do that since the constructor in the ``PiRouter`` class takes arguments, instead we need to use the ``UntypedActorFactory`` which unfortunately is a bit more verbose.
|
||||
|
||||
``actorOf`` is the only way to create an instance of an Actor, this is enforced by Akka runtime. The ``actorOf`` method instantiates the actor and returns, not an instance to the actor, but an instance to an ``ActorRef``. This reference is the handle through which you communicate with the actor. It is immutable, serializable and location-aware meaning that it "remembers" its original actor even if it is sent to other nodes across the network and can be seen as the equivalent to the Erlang actor's PID.
|
||||
|
||||
The actor's life-cycle is:
|
||||
|
||||
- Created & Started -- ``Actor.actorOf(Props[MyActor])`` -- can receive messages
|
||||
- Stopped -- ``actorRef.stop()`` -- can **not** receive messages
|
||||
|
||||
Once the actor has been stopped it is dead and can not be started again.
|
||||
|
||||
Now we have a router that is representing all our workers in a single abstraction. If you paid attention to the code above, you saw that we were using the ``nrOfWorkers`` variable. This variable and others we have to pass to the ``Master`` actor in its constructor. So now let's create the master actor. We have to pass in three integer variables:
|
||||
Now we have a router that is representing all our workers in a single
|
||||
abstraction. So now let's create the master actor. We pass it three integer variables:
|
||||
|
||||
- ``nrOfWorkers`` -- defining how many workers we should start up
|
||||
- ``nrOfMessages`` -- defining how many number chunks to send out to the workers
|
||||
- ``nrOfElements`` -- defining how big the number chunks sent to each worker should be
|
||||
|
||||
Here is the master actor::
|
||||
Here is the master actor:
|
||||
|
||||
static class Master extends UntypedActor {
|
||||
private final int nrOfMessages;
|
||||
private final int nrOfElements;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
private double pi;
|
||||
private int nrOfResults;
|
||||
private long start;
|
||||
|
||||
private ActorRef router;
|
||||
|
||||
static class PiRouter extends UntypedLoadBalancer {
|
||||
private final InfiniteIterator<ActorRef> workers;
|
||||
|
||||
public PiRouter(ActorRef[] workers) {
|
||||
this.workers = new CyclicIterator<ActorRef>(asList(workers));
|
||||
}
|
||||
|
||||
public InfiniteIterator<ActorRef> seq() {
|
||||
return workers;
|
||||
}
|
||||
}
|
||||
|
||||
public Master(
|
||||
int nrOfWorkers, int nrOfMessages, int nrOfElements, CountDownLatch latch) {
|
||||
this.nrOfMessages = nrOfMessages;
|
||||
this.nrOfElements = nrOfElements;
|
||||
this.latch = latch;
|
||||
|
||||
// create the workers
|
||||
final ActorRef[] workers = new ActorRef[nrOfWorkers];
|
||||
for (int i = 0; i < nrOfWorkers; i++) {
|
||||
workers[i] = actorOf(new Props(Worker.class));
|
||||
}
|
||||
|
||||
// wrap them with a load-balancing router
|
||||
router = actorOf(new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new PiRouter(workers);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// message handler
|
||||
public void onReceive(Object message) { ... }
|
||||
|
||||
@Override
|
||||
public void preStart() {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
System.out.println(String.format(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis",
|
||||
pi, (System.currentTimeMillis() - start)));
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#master
|
||||
:exclude: handle-messages
|
||||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``ask()`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the
|
||||
``Master`` actor. This latch is only used for plumbing (in this specific
|
||||
tutorial), to have a simple way of letting the outside world knowing when the
|
||||
master can deliver the result and shut down. In more idiomatic Akka code
|
||||
we would not use a latch but other abstractions and functions like ``Future``
|
||||
and ``ask()`` to achieve the same thing in a non-blocking way.
|
||||
But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and
|
||||
``postStop``. In the ``preStart`` callback we are recording the time when the
|
||||
actor is started and in the ``postStop`` callback we are printing out the result
|
||||
(the approximation of Pi) and the time it took to calculate it. In this call we
|
||||
also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
|
||||
But we are not done yet. We are missing the message handler for the ``Master`` actor. This message handler needs to be able to react to two different messages:
|
||||
But we are not done yet. We are missing the message handler for the ``Master`` actor.
|
||||
This message handler needs to be able to react to two different messages:
|
||||
|
||||
- ``Calculate`` -- which should start the calculation
|
||||
- ``Result`` -- which should aggregate the different results
|
||||
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` actors and after doing that it also sends a ``new Broadcast(poisonPill())`` message to the router, which will send out the ``PoisonPill`` message to all the actors it is representing (in our case all the ``Worker`` actors). ``PoisonPill`` is a special kind of message that tells the receiver to shut itself down using the normal shutdown method; ``getContext().stop()``, and is created through the ``poisonPill()`` method. We also send a ``PoisonPill`` to the router itself (since it's also an actor that we want to shut down).
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` via its router.
|
||||
|
||||
The ``Result`` handler is simpler, here we get the value from the ``Result`` message and aggregate it to our ``pi`` member variable. We also keep track of how many results we have received back, and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and shuts down.
|
||||
The ``Result`` handler gets the value from the ``Result`` message and aggregates it to
|
||||
our ``pi`` member variable. We also keep track of how many results we have received back,
|
||||
and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and
|
||||
invokes the ``self.stop()`` method to stop itself *and* all its supervised actors.
|
||||
In this case it has one supervised actor, the router, and this in turn has ``nrOfWorkers`` supervised actors.
|
||||
All of them will be stopped automatically as the invocation of any supervisor's ``stop`` method
|
||||
will propagate down to all its supervised 'children'.
|
||||
|
||||
Let's capture this in code::
|
||||
Let's capture this in code:
|
||||
|
||||
// message handler
|
||||
public void onReceive(Object message) {
|
||||
|
||||
if (message instanceof Calculate) {
|
||||
// schedule work
|
||||
for (int start = 0; start < nrOfMessages; start++) {
|
||||
router.tell(new Work(start, nrOfElements), getContext());
|
||||
}
|
||||
|
||||
// send a PoisonPill to all workers telling them to shut down themselves
|
||||
router.tell(new Broadcast(poisonPill()));
|
||||
|
||||
// send a PoisonPill to the router, telling him to shut himself down
|
||||
router.tell(poisonPill());
|
||||
|
||||
} else if (message instanceof Result) {
|
||||
|
||||
// handle result from the worker
|
||||
Result result = (Result) message;
|
||||
pi += result.getValue();
|
||||
nrOfResults += 1;
|
||||
if (nrOfResults == nrOfMessages) getContext().stop();
|
||||
|
||||
} else throw new IllegalArgumentException("Unknown message [" + message + "]");
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#master-receive
|
||||
|
||||
Bootstrap the calculation
|
||||
-------------------------
|
||||
|
||||
Now the only thing that is left to implement is the runner that should bootstrap and run the calculation for us. We do that by adding a ``main`` method to the enclosing ``Pi`` class in which we create a new instance of ``Pi`` and invoke method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish::
|
||||
Now the only thing that is left to implement is the runner that should bootstrap and run the calculation for us.
|
||||
We do that by adding a ``main`` method to the enclosing ``Pi`` class in which we create a new instance of ``Pi`` and
|
||||
invoke method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish:
|
||||
|
||||
public class Pi {
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java#app
|
||||
:exclude: actors-and-messages
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Pi pi = new Pi();
|
||||
pi.calculate(4, 10000, 10000);
|
||||
}
|
||||
|
||||
public void calculate(final int nrOfWorkers, final int nrOfElements, final int nrOfMessages)
|
||||
throws Exception {
|
||||
|
||||
// this latch is only plumbing to know when the calculation is completed
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
// create the master
|
||||
ActorRef master = actorOf(new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new Master(nrOfWorkers, nrOfMessages, nrOfElements, latch);
|
||||
}
|
||||
}));
|
||||
|
||||
// start the calculation
|
||||
master.tell(new Calculate());
|
||||
|
||||
// wait for master to shut down
|
||||
latch.await();
|
||||
}
|
||||
}
|
||||
As you can see the *calculate* method above it creates an ActorSystem and this is the Akka container which
|
||||
will contain all actors created in that "context". An example of how to create actors in the container
|
||||
is the *'system.actorOf(...)'* line in the calculate method. In this case we create a top level actor.
|
||||
If you instead where in an actor context, i.e. inside an actor creating other actors, you should use
|
||||
*this.getContext.actorOf(...)*. This is illustrated in the Master code above.
|
||||
|
||||
That's it. Now we are done.
|
||||
|
||||
Before we package it up and run it, let's take a look at the full code now, with package declaration, imports and all::
|
||||
|
||||
package akka.tutorial.first.java;
|
||||
|
||||
import static akka.actor.Actors.actorOf;
|
||||
import static akka.actor.Actors.poisonPill;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import akka.actor.Props;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.routing.CyclicIterator;
|
||||
import akka.routing.InfiniteIterator;
|
||||
import akka.routing.Routing.Broadcast;
|
||||
import akka.routing.UntypedLoadBalancer;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class Pi {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Pi pi = new Pi();
|
||||
pi.calculate(4, 10000, 10000);
|
||||
}
|
||||
|
||||
// ====================
|
||||
// ===== Messages =====
|
||||
// ====================
|
||||
static class Calculate {}
|
||||
|
||||
static class Work {
|
||||
private final int start;
|
||||
private final int nrOfElements;
|
||||
|
||||
public Work(int start, int nrOfElements) {
|
||||
this.start = start;
|
||||
this.nrOfElements = nrOfElements;
|
||||
}
|
||||
|
||||
public int getStart() { return start; }
|
||||
public int getNrOfElements() { return nrOfElements; }
|
||||
}
|
||||
|
||||
static class Result {
|
||||
private final double value;
|
||||
|
||||
public Result(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public double getValue() { return value; }
|
||||
}
|
||||
|
||||
// ==================
|
||||
// ===== Worker =====
|
||||
// ==================
|
||||
static class Worker extends UntypedActor {
|
||||
|
||||
// define the work
|
||||
private double calculatePiFor(int start, int nrOfElements) {
|
||||
double acc = 0.0;
|
||||
for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) {
|
||||
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// message handler
|
||||
public void onReceive(Object message) {
|
||||
if (message instanceof Work) {
|
||||
Work work = (Work) message;
|
||||
|
||||
// perform the work
|
||||
double result = calculatePiFor(work.getStart(), work.getNrOfElements())
|
||||
|
||||
// reply with the result
|
||||
getContext().reply(new Result(result));
|
||||
|
||||
} else throw new IllegalArgumentException("Unknown message [" + message + "]");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================
|
||||
// ===== Master =====
|
||||
// ==================
|
||||
static class Master extends UntypedActor {
|
||||
private final int nrOfMessages;
|
||||
private final int nrOfElements;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
private double pi;
|
||||
private int nrOfResults;
|
||||
private long start;
|
||||
|
||||
private ActorRef router;
|
||||
|
||||
static class PiRouter extends UntypedLoadBalancer {
|
||||
private final InfiniteIterator<ActorRef> workers;
|
||||
|
||||
public PiRouter(ActorRef[] workers) {
|
||||
this.workers = new CyclicIterator<ActorRef>(asList(workers));
|
||||
}
|
||||
|
||||
public InfiniteIterator<ActorRef> seq() {
|
||||
return workers;
|
||||
}
|
||||
}
|
||||
|
||||
public Master(
|
||||
int nrOfWorkers, int nrOfMessages, int nrOfElements, CountDownLatch latch) {
|
||||
|
||||
this.nrOfMessages = nrOfMessages;
|
||||
this.nrOfElements = nrOfElements;
|
||||
this.latch = latch;
|
||||
|
||||
// create the workers
|
||||
final ActorRef[] workers = new ActorRef[nrOfWorkers];
|
||||
for (int i = 0; i < nrOfWorkers; i++) {
|
||||
workers[i] = actorOf(new Props(Worker.class));
|
||||
}
|
||||
|
||||
// wrap them with a load-balancing router
|
||||
router = actorOf(new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new PiRouter(workers);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// message handler
|
||||
public void onReceive(Object message) {
|
||||
|
||||
if (message instanceof Calculate) {
|
||||
// schedule work
|
||||
for (int start = 0; start < nrOfMessages; start++) {
|
||||
router.tell(new Work(start, nrOfElements), getContext());
|
||||
}
|
||||
|
||||
// send a PoisonPill to all workers telling them to shut down themselves
|
||||
router.tell(new Broadcast(poisonPill()));
|
||||
|
||||
// send a PoisonPill to the router, telling him to shut himself down
|
||||
router.tell(poisonPill());
|
||||
|
||||
} else if (message instanceof Result) {
|
||||
|
||||
// handle result from the worker
|
||||
Result result = (Result) message;
|
||||
pi += result.getValue();
|
||||
nrOfResults += 1;
|
||||
if (nrOfResults == nrOfMessages) getContext().stop();
|
||||
|
||||
} else throw new IllegalArgumentException("Unknown message [" + message + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStart() {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
System.out.println(String.format(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis",
|
||||
pi, (System.currentTimeMillis() - start)));
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================
|
||||
// ===== Run it =====
|
||||
// ==================
|
||||
public void calculate(final int nrOfWorkers, final int nrOfElements, final int nrOfMessages)
|
||||
throws Exception {
|
||||
|
||||
// this latch is only plumbing to know when the calculation is completed
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
// create the master
|
||||
ActorRef master = actorOf(new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new Master(nrOfWorkers, nrOfMessages, nrOfElements, latch);
|
||||
}
|
||||
}));
|
||||
|
||||
// start the calculation
|
||||
master.tell(new Calculate());
|
||||
|
||||
// wait for master to shut down
|
||||
latch.await();
|
||||
}
|
||||
}
|
||||
Before we package it up and run it, let's take a look at the full code now, with package declaration, imports and all:
|
||||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/java/akka/tutorial/first/java/Pi.java
|
||||
|
||||
Run it as a command line application
|
||||
------------------------------------
|
||||
|
|
@ -729,11 +363,11 @@ and the ``scala-library.jar`` JAR files to the classpath as well as the classes
|
|||
we compiled ourselves::
|
||||
|
||||
$ java \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-2.0-SNAPSHOT.jar:tutorial \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-2.0-SNAPSHOT.jar:. \
|
||||
akka.tutorial.java.first.Pi
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 822 millis
|
||||
Calculation time: 609 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
|
|
@ -750,7 +384,7 @@ When this in done we can run our application directly inside Maven::
|
|||
$ mvn exec:java -Dexec.mainClass="akka.tutorial.first.java.Pi"
|
||||
...
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 939 millis
|
||||
Calculation time: 597 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,32 +6,69 @@ Getting Started Tutorial (Scala with Eclipse): First Chapter
|
|||
Introduction
|
||||
------------
|
||||
|
||||
Welcome to the first tutorial on how to get started with `Akka <http://akka.io>`_ and `Scala <http://scala-lang.org>`_. We assume that you already know what Akka and Scala are and will now focus on the steps necessary to start your first project. We will be using `Eclipse <http://www.eclipse.org/downloads/>`_, and the `Scala plugin for Eclipse <http://www.scala-ide.org/>`_.
|
||||
Welcome to the first tutorial on how to get started with Akka and Scala. We
|
||||
assume that you already know what Akka and Scala are and will now focus on the
|
||||
steps necessary to start your first project.
|
||||
|
||||
The sample application that we will create is using actors to calculate the value of Pi. Calculating Pi is a CPU intensive operation and we will utilize Akka Actors to write a concurrent solution that scales out to multi-core processors. This sample will be extended in future tutorials to use Akka Remote Actors to scale out on multiple machines in a cluster.
|
||||
There are two variations of this first tutorial:
|
||||
|
||||
We will be using an algorithm that is called "embarrassingly parallel" which just means that each job is completely isolated and not coupled with any other job. Since this algorithm is so parallelizable it suits the actor model very well.
|
||||
- creating a standalone project and run it from the command line
|
||||
- creating a SBT (Simple Build Tool) project and running it from within SBT
|
||||
|
||||
Since they are so similar we will present them both.
|
||||
|
||||
The sample application that we will create is using actors to calculate the
|
||||
value of Pi. Calculating Pi is a CPU intensive operation and we will utilize
|
||||
Akka Actors to write a concurrent solution that scales out to multi-core
|
||||
processors. This sample will be extended in future tutorials to use Akka Remote
|
||||
Actors to scale out on multiple machines in a cluster.
|
||||
|
||||
We will be using an algorithm that is called "embarrassingly parallel" which
|
||||
just means that each job is completely isolated and not coupled with any other
|
||||
job. Since this algorithm is so parallelizable it suits the actor model very
|
||||
well.
|
||||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: ../images/pi-formula.png
|
||||
|
||||
In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result.
|
||||
In this particular algorithm the master splits the series into chunks which are
|
||||
sent out to each worker actor to be processed. When each worker has processed
|
||||
its chunk it sends a result back to the master which aggregates the total
|
||||
result.
|
||||
|
||||
|
||||
Tutorial source code
|
||||
--------------------
|
||||
|
||||
If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__.
|
||||
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
|
||||
-------------
|
||||
|
||||
This tutorial assumes that you have Java 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a recent version of Eclipse (at least `3.6 - Helios <http://www.eclipse.org/downloads/>`_).
|
||||
This tutorial assumes that you have Java 1.6 or later installed on you machine
|
||||
and ``java`` on your ``PATH``. You also need to know how to run commands in a
|
||||
shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala
|
||||
code.
|
||||
|
||||
If you want to run the example from the command line as well, you need to make sure that ``$JAVA_HOME`` environment variable is set to the root of the Java distribution. You also need to make sure that the ``$JAVA_HOME/bin`` is on your ``PATH``::
|
||||
You need to make sure that ``$JAVA_HOME`` environment variable is set to the
|
||||
root of the Java distribution. You also need to make sure that the
|
||||
``$JAVA_HOME/bin`` is on your ``PATH``::
|
||||
|
||||
$ export JAVA_HOME=..root of java distribution..
|
||||
$ export PATH=$PATH:$JAVA_HOME/bin
|
||||
|
|
@ -48,10 +85,10 @@ Downloading and installing Akka
|
|||
-------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to download
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skip
|
||||
this section and jump to the next one.
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skip this
|
||||
section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
Let's get the ``akka-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -62,103 +99,138 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ cd /Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
/Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
$ ls -1
|
||||
bin
|
||||
config
|
||||
deploy
|
||||
doc
|
||||
lib
|
||||
src
|
||||
|
||||
- In the ``bin`` directory we have scripts for starting the Akka Microkernel.
|
||||
- In the ``config`` directory we have the Akka conf files.
|
||||
- In the ``doc`` directory we have the documentation, API, doc JARs, and also
|
||||
the source files for the tutorials.
|
||||
- In the ``deploy`` directory we can place applications to be run with the microkernel.
|
||||
- In the ``doc`` directory we have the documentation, API, and doc JARs.
|
||||
- In the ``lib`` directory we have the Scala and Akka JARs.
|
||||
- In the ``src`` directory we have the source JARs for Akka.
|
||||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
Akka is very modular and has many JARs for containing different features. The
|
||||
modules are:
|
||||
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor`` -- Actors
|
||||
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
- ``akka-remote`` -- Remote Actors
|
||||
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
- ``akka-slf4j`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
|
||||
- ``akka-testkit`` -- Toolkit for testing Actors
|
||||
|
||||
- ``akka-kernel`` -- Akka microkernel for running a bare-bones mini application server
|
||||
|
||||
- ``akka-durable-mailboxes`` -- Durable mailboxes: file-based, MongoDB, Redis, Zookeeper
|
||||
|
||||
- ``akka-amqp`` -- AMQP integration
|
||||
|
||||
.. - ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
.. - ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
.. - ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
.. - ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing the Scala IDE for Eclipse
|
||||
----------------------------------------------------
|
||||
|
||||
If you want to use Eclipse for coding your Akka tutorial, you need to install the Scala plugin for Eclipse. This plugin comes with its own version of Scala, so if you don't plan to run the example from the command line, you don't need to download the Scala distribution (and you can skip the next section).
|
||||
If you want to use Eclipse for coding your Akka tutorial, you need to install the Scala plugin for Eclipse.
|
||||
This plugin comes with its own version of Scala, so if you don't plan to run the example from the command line,
|
||||
you don't need to download the Scala distribution (and you can skip the next section).
|
||||
|
||||
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.
|
||||
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:: ../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.
|
||||
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.
|
||||
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:: ../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.
|
||||
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:: ../images/example-code.png
|
||||
|
||||
You are ready to code now!
|
||||
|
||||
|
||||
Downloading and installing Scala
|
||||
--------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to install the Scala distribution. If you prefer to use Eclipse to build and run the sample then you can skip this section and jump to the next one.
|
||||
To build and run the tutorial sample from the command line, you have to install
|
||||
the Scala distribution. If you prefer to use SBT to build and run the sample
|
||||
then you can skip this section and jump to the next one.
|
||||
|
||||
Scala can be downloaded from `http://www.scala-lang.org/downloads <http://www.scala-lang.org/downloads>`_. Browse there and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions.
|
||||
Scala can be downloaded from http://www.scala-lang.org/downloads. Browse there
|
||||
and download the Scala 2.9.1 release. If you pick the ``tgz`` or ``zip``
|
||||
distribution then just unzip it where you want it installed. If you pick the
|
||||
IzPack Installer then double click on it and follow the instructions.
|
||||
|
||||
You also need to make sure that the ``scala-2.9.0/bin`` (if that is the directory where you installed Scala) is on your ``PATH``::
|
||||
You also need to make sure that the ``scala-2.9.1/bin`` (if that is the
|
||||
directory where you installed Scala) is on your ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:scala-2.9.0/bin
|
||||
$ export PATH=$PATH:scala-2.9.1/bin
|
||||
|
||||
You can test your installation by invoking scala::
|
||||
|
||||
$ scala -version
|
||||
Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL
|
||||
Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
|
||||
|
||||
Looks like we are all good. Finally let's create a source file ``Pi.scala`` for the tutorial and put it in the root of the Akka distribution in the ``tutorial`` directory (you have to create it first).
|
||||
Looks like we are all good. Finally let's create a source file ``Pi.scala`` for
|
||||
the tutorial and put it in the root of the Akka distribution in the ``tutorial``
|
||||
directory (you have to create it first).
|
||||
|
||||
Some tools require you to set the ``SCALA_HOME`` environment variable to the
|
||||
root of the Scala distribution, however Akka does not require that.
|
||||
|
||||
Some tools require you to set the ``SCALA_HOME`` environment variable to the root of the Scala distribution, however Akka does not require that.
|
||||
|
||||
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:
|
||||
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:: ../images/build-path.png
|
||||
|
||||
|
||||
Using SBT in Eclipse
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you are an `SBT <https://github.com/harrah/xsbt/wiki>`_ user, you can follow the :ref:`getting-started-first-scala-download-sbt` instruction and additionally install the ``sbteclipse`` plugin. This adds support for generating Eclipse project files from your SBT project.
|
||||
You need to install the plugin as described in the `README of sbteclipse <https://github.com/typesafehub/sbteclipse>`_
|
||||
If you are an `SBT <https://github.com/harrah/xsbt/wiki>`_ user, you can follow the :ref:`getting-started-first-scala-download-sbt`
|
||||
instruction and additionally install the ``sbteclipse`` plugin. This adds support for generating Eclipse project files
|
||||
from your SBT project. You need to install the plugin as described in the `README of sbteclipse
|
||||
<https://github.com/typesafehub/sbteclipse>`_
|
||||
|
||||
Then run the ``eclipse`` target to generate the Eclipse project::
|
||||
|
||||
|
|
@ -173,12 +245,14 @@ The options `create-src` and `with-sources` are useful::
|
|||
* create-src to create the common source directories, e.g. src/main/scala, src/main/test
|
||||
* with-sources to create source attachments for the library dependencies
|
||||
|
||||
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:
|
||||
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:: ../images/import-project.png
|
||||
|
||||
Now we have the basis for an Akka Eclipse application, so we can..
|
||||
|
||||
|
||||
Start writing the code
|
||||
----------------------
|
||||
|
||||
|
|
@ -186,10 +260,14 @@ The design we are aiming for is to have one ``Master`` actor initiating the comp
|
|||
|
||||
With this in mind, let's now create the messages that we want to have flowing in the system.
|
||||
|
||||
|
||||
Creating the messages
|
||||
---------------------
|
||||
|
||||
We start by creating a package for our application, let's call it ``akka.tutorial.first.scala``. We start by creating case classes for each type of message in our application, so we can place them in a hierarchy, call it ``PiMessage``. Right click on the package and choose ``New Scala Class``, and enter ``PiMessage`` for the name of the class.
|
||||
We start by creating a package for our application, let's call it ``akka.tutorial.first.scala``.
|
||||
We start by creating case classes for each type of message in our application, so we can place them in a hierarchy,
|
||||
call it ``PiMessage``. Right click on the package and choose ``New Scala Class``, and enter ``PiMessage`` as
|
||||
the name of the class.
|
||||
|
||||
We need three different messages:
|
||||
|
||||
|
|
@ -197,204 +275,156 @@ We need three different messages:
|
|||
- ``Work`` -- sent from the ``Master`` actor to the ``Worker`` actors containing the work assignment
|
||||
- ``Result`` -- sent from the ``Worker`` actors to the ``Master`` actor containing the result from the worker's calculation
|
||||
|
||||
Messages sent to actors should always be immutable to avoid sharing mutable state. In Scala we have 'case classes' which make excellent messages. So let's start by creating three messages as case classes. We also create a common base trait for our messages (that we define as being ``sealed`` in order to prevent creating messages outside our control)::
|
||||
Messages sent to actors should always be immutable to avoid sharing mutable state.
|
||||
In Scala we have 'case classes' which make excellent messages. So let's start by creating three messages as case classes.
|
||||
We also create a common base trait for our messages (that we define as being ``sealed`` in order to prevent creating messages
|
||||
outside our control):
|
||||
|
||||
package akka.tutorial.first.scala
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#messages
|
||||
|
||||
sealed trait PiMessage
|
||||
|
||||
case object Calculate extends PiMessage
|
||||
|
||||
case class Work(start: Int, nrOfElements: Int) extends PiMessage
|
||||
|
||||
case class Result(value: Double) extends PiMessage
|
||||
|
||||
Creating the worker
|
||||
-------------------
|
||||
|
||||
Now we can create the worker actor. Create a new class called ``Worker`` as before. We need to mix in the ``Actor`` trait and defining the ``receive`` method. The ``receive`` method defines our message handler. We expect it to be able to handle the ``Work`` message so we need to add a handler for this message::
|
||||
Now we can create the worker actor. This is done by mixing in the ``Actor``
|
||||
trait and defining the ``receive`` method. The ``receive`` method defines our
|
||||
message handler. We expect it to be able to handle the ``Work`` message so we
|
||||
need to add a handler for this message:
|
||||
|
||||
class Worker extends Actor {
|
||||
def receive = {
|
||||
case Work(start, nrOfElements) =>
|
||||
self reply Result(calculatePiFor(start, nrOfElements)) // perform the work
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#worker
|
||||
:exclude: calculatePiFor
|
||||
|
||||
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:
|
||||
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:: ../images/quickfix.png
|
||||
|
||||
Choose the Akka Actor and move on.
|
||||
|
||||
As you can see we have now created an ``Actor`` with a ``receive`` method as a handler for the ``Work`` message. In this handler we invoke the ``calculatePiFor(..)`` method, wrap the result in a ``Result`` message and send it back to the original sender using ``self.reply``. In Akka the sender reference is implicitly passed along with the message so that the receiver can always reply or store away the sender reference for future use.
|
||||
As you can see we have now created an ``Actor`` with a ``receive`` method as a
|
||||
handler for the ``Work`` message. In this handler we invoke the
|
||||
``calculatePiFor(..)`` method, wrap the result in a ``Result`` message and send
|
||||
it back asynchronously to the original sender using the ``sender`` reference.
|
||||
In Akka the sender reference is implicitly passed along with the message so that
|
||||
the receiver can always reply or store away the sender reference for future use.
|
||||
|
||||
The only thing missing in our ``Worker`` actor is the implementation on the ``calculatePiFor(..)`` method. While there are many ways we can implement this algorithm in Scala, in this introductory tutorial we have chosen an imperative style using a for comprehension and an accumulator::
|
||||
The only thing missing in our ``Worker`` actor is the implementation on the
|
||||
``calculatePiFor(..)`` method. While there are many ways we can implement this
|
||||
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
||||
style using a for comprehension and an accumulator:
|
||||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#calculatePiFor
|
||||
|
||||
def calculatePiFor(start: Int, nrOfElements: Int): Double = {
|
||||
var acc = 0.0
|
||||
for (i <- start until (start + nrOfElements))
|
||||
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)
|
||||
acc
|
||||
}
|
||||
|
||||
Creating the master
|
||||
-------------------
|
||||
|
||||
Now create a new class for the master actor. The master actor is a little bit more involved. In its constructor we need to create the workers (the ``Worker`` actors) and start them. We will also wrap them in a load-balancing router to make it easier to spread out the work evenly between the workers. First we need to add some imports::
|
||||
Now create a new class for the master actor. The master actor is a little bit
|
||||
more involved. In its constructor we create a round-robin router to make it easier
|
||||
to spread out the work evenly between the workers. First we need to add some imports:
|
||||
|
||||
import akka.actor.{Actor, PoisonPill}
|
||||
import akka.routing.{Routing, CyclicIterator}
|
||||
import Routing._
|
||||
import akka.dispatch.Dispatchers
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#imports
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
and then we can create the router:
|
||||
|
||||
and then we can create the workers::
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#create-router
|
||||
|
||||
// create the workers
|
||||
val workers = Vector.fill(nrOfWorkers)(actorOf(Props[Worker])
|
||||
|
||||
// wrap them with a load-balancing router
|
||||
val router = Routing.loadBalancerActor(CyclicIterator(workers))
|
||||
|
||||
As you can see we are using the ``actorOf`` factory method to create actors, this method returns as an ``ActorRef`` which is a reference to our newly created actor. This method is available in the ``Actor`` object but is usually imported::
|
||||
|
||||
import akka.actor.Actor.actorOf
|
||||
|
||||
There are two versions of ``actorOf``; one of them taking a actor type and the other one an instance of an actor. The former one (``actorOf(Props[MyActor]``) is used when the actor class has a no-argument constructor while the second one (``actorOf(Props(new MyActor(..))``) is used when the actor class has a constructor that takes arguments. This is the only way to create an instance of an Actor and the ``actorOf`` method ensures this. The latter version is using call-by-name and lazily creates the actor within the scope of the ``actorOf`` method. The ``actorOf`` method instantiates the actor and returns, not an instance to the actor, but an instance to an ``ActorRef``. This reference is the handle through which you communicate with the actor. It is immutable, serializable and location-aware meaning that it "remembers" its original actor even if it is sent to other nodes across the network and can be seen as the equivalent to the Erlang actor's PID.
|
||||
|
||||
The actor's life-cycle is:
|
||||
|
||||
- Created -- ``Actor.actorOf(Props[MyActor]`` -- can **not** receive messages
|
||||
- Started -- ``actorRef`` -- can receive messages
|
||||
- Stopped -- ``actorRef.stop()`` -- can **not** receive messages
|
||||
|
||||
Once the actor has been stopped it is dead and can not be started again.
|
||||
|
||||
Now we have a router that is representing all our workers in a single abstraction. If you paid attention to the code above, you saw that we were using the ``nrOfWorkers`` variable. This variable and others we have to pass to the ``Master`` actor in its constructor. So now let's create the master actor. We have to pass in three integer variables:
|
||||
Now we have a router that is representing all our workers in a single
|
||||
abstraction. So now let's create the master actor. We pass it three integer variables:
|
||||
|
||||
- ``nrOfWorkers`` -- defining how many workers we should start up
|
||||
- ``nrOfMessages`` -- defining how many number chunks to send out to the workers
|
||||
- ``nrOfElements`` -- defining how big the number chunks sent to each worker should be
|
||||
|
||||
Here is the master actor::
|
||||
Here is the master actor:
|
||||
|
||||
class Master(
|
||||
nrOfWorkers: Int, nrOfMessages: Int, nrOfElements: Int, latch: CountDownLatch)
|
||||
extends Actor {
|
||||
|
||||
var pi: Double = _
|
||||
var nrOfResults: Int = _
|
||||
var start: Long = _
|
||||
|
||||
// create the workers
|
||||
val workers = Vector.fill(nrOfWorkers)(actorOf(Props[Worker])
|
||||
|
||||
// wrap them with a load-balancing router
|
||||
val router = Routing.loadBalancerActor(CyclicIterator(workers))
|
||||
|
||||
def receive = { ... }
|
||||
|
||||
override def preStart() {
|
||||
start = System.currentTimeMillis
|
||||
}
|
||||
|
||||
override def postStop() {
|
||||
// tell the world that the calculation is complete
|
||||
println(
|
||||
"\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis"
|
||||
.format(pi, (System.currentTimeMillis - start)))
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master
|
||||
:exclude: handle-messages
|
||||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the
|
||||
``Master`` actor. This latch is only used for plumbing (in this specific
|
||||
tutorial), to have a simple way of letting the outside world knowing when the
|
||||
master can deliver the result and shut down. In more idiomatic Akka code
|
||||
we would not use a latch but other abstractions and functions like ``Future``
|
||||
and ``?`` to achieve the same thing in a non-blocking way.
|
||||
But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done.
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and
|
||||
``postStop``. In the ``preStart`` callback we are recording the time when the
|
||||
actor is started and in the ``postStop`` callback we are printing out the result
|
||||
(the approximation of Pi) and the time it took to calculate it. In this call we
|
||||
also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
|
||||
But we are not done yet. We are missing the message handler for the ``Master`` actor. This message handler needs to be able to react to two different messages:
|
||||
But we are not done yet. We are missing the message handler for the ``Master``
|
||||
actor. This message handler needs to be able to react to two different messages:
|
||||
|
||||
- ``Calculate`` -- which should start the calculation
|
||||
- ``Result`` -- which should aggregate the different results
|
||||
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` actors and after doing that it also sends a ``Broadcast(PoisonPill)`` message to the router, which will send out the ``PoisonPill`` message to all the actors it is representing (in our case all the ``Worker`` actors). ``PoisonPill`` is a special kind of message that tells the receiver to shut itself down using the normal shutdown method; ``self.stop``. We also send a ``PoisonPill`` to the router itself (since it's also an actor that we want to shut down).
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` via its router.
|
||||
|
||||
The ``Result`` handler is simpler, here we get the value from the ``Result`` message and aggregate it to our ``pi`` member variable. We also keep track of how many results we have received back, and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and shuts down.
|
||||
The ``Result`` handler gets the value from the ``Result`` message and aggregates it to
|
||||
our ``pi`` member variable. We also keep track of how many results we have received back,
|
||||
and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and
|
||||
invokes the ``self.stop()`` method to stop itself *and* all its supervised actors.
|
||||
In this case it has one supervised actor, the router, and this in turn has ``nrOfWorkers`` supervised actors.
|
||||
All of them will be stopped automatically as the invocation of any supervisor's ``stop`` method
|
||||
will propagate down to all its supervised 'children'.
|
||||
|
||||
Let's capture this in code::
|
||||
Let's capture this in code:
|
||||
|
||||
// message handler
|
||||
def receive = {
|
||||
case Calculate =>
|
||||
// schedule work
|
||||
for (i <- 0 until nrOfMessages) router ! Work(i * nrOfElements, nrOfElements)
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
|
||||
|
||||
// send a PoisonPill to all workers telling them to shut down themselves
|
||||
router ! Broadcast(PoisonPill)
|
||||
|
||||
// send a PoisonPill to the router, telling him to shut himself down
|
||||
router ! PoisonPill
|
||||
|
||||
case Result(value) =>
|
||||
// handle result from the worker
|
||||
pi += value
|
||||
nrOfResults += 1
|
||||
if (nrOfResults == nrOfMessages) self.stop()
|
||||
}
|
||||
|
||||
Bootstrap the calculation
|
||||
-------------------------
|
||||
|
||||
Now the only thing that is left to implement is the runner that should bootstrap and run the calculation for us. We do that by creating an object that we call ``Pi``, here we can extend the ``App`` trait in Scala, which means that we will be able to run this as an application directly from the command line or using the Eclipse Runner.
|
||||
Now the only thing that is left to implement is the runner that should bootstrap and run the calculation for us.
|
||||
We do that by creating an object that we call ``Pi``, here we can extend the ``App`` trait in Scala,
|
||||
which means that we will be able to run this as an application directly from the command line or using the Eclipse Runner.
|
||||
|
||||
The ``Pi`` object is a perfect container module for our actors and messages, so let's put them all there. We also create a method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish::
|
||||
The ``Pi`` object is a perfect container module for our actors and messages, so let's put them all there.
|
||||
We also create a method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish:
|
||||
|
||||
object Pi extends App {
|
||||
|
||||
calculate(nrOfWorkers = 4, nrOfElements = 10000, nrOfMessages = 10000)
|
||||
|
||||
... // actors and messages
|
||||
|
||||
def calculate(nrOfWorkers: Int, nrOfElements: Int, nrOfMessages: Int) {
|
||||
|
||||
// this latch is only plumbing to know when the calculation is completed
|
||||
val latch = new CountDownLatch(1)
|
||||
|
||||
// create the master
|
||||
val master = actorOf(Props(new Master(nrOfWorkers, nrOfMessages, nrOfElements, latch)))
|
||||
|
||||
// start the calculation
|
||||
master ! Calculate
|
||||
|
||||
// wait for master to shut down
|
||||
latch.await()
|
||||
}
|
||||
}
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
|
||||
:exclude: actors-and-messages
|
||||
|
||||
That's it. Now we are done.
|
||||
|
||||
Run it from Eclipse
|
||||
-------------------
|
||||
|
||||
Eclipse builds your project on every save when ``Project/Build Automatically`` is set. If not, bring you project up to date by clicking ``Project/Build Project``. If there are no compilation errors, you can right-click in the editor where ``Pi`` is defined, and choose ``Run as.. /Scala application``. If everything works fine, you should see::
|
||||
Eclipse builds your project on every save when ``Project/Build Automatically`` is set.
|
||||
If not, bring you project up to date by clicking ``Project/Build Project``. If there are no compilation errors,
|
||||
you can right-click in the editor where ``Pi`` is defined, and choose ``Run as.. /Scala application``.
|
||||
If everything works fine, you should see::
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 858 millis
|
||||
Calculation time: 632 millis
|
||||
|
||||
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 :ref:`configuration` is:
|
||||
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 :ref:`configuration` is:
|
||||
|
||||
.. 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``.
|
||||
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``.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
We have learned how to create our first Akka project using Akka's actors to speed up a computation-intensive problem by scaling out on multi-core processors (also known as scaling up). We have also learned to compile and run an Akka project using Eclipse.
|
||||
We have learned how to create our first Akka project using Akka's actors to
|
||||
speed up a computation-intensive problem by scaling out on multi-core processors
|
||||
(also known as scaling up). We have also learned to compile and run an Akka
|
||||
project using either the tools on the command line or the SBT build system.
|
||||
|
||||
If you have a multi-core machine then I encourage you to try out different number of workers (number of working actors) by tweaking the ``nrOfWorkers`` variable to for example; 2, 4, 6, 8 etc. to see performance improvement by scaling up.
|
||||
If you have a multi-core machine then I encourage you to try out different
|
||||
number of workers (number of working actors) by tweaking the ``nrOfWorkers``
|
||||
variable to for example; 2, 4, 6, 8 etc. to see performance improvement by
|
||||
scaling up.
|
||||
|
||||
Happy hakking.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,24 @@ sent out to each worker actor to be processed. When each worker has processed
|
|||
its chunk it sends a result back to the master which aggregates the total
|
||||
result.
|
||||
|
||||
Tutorial source code
|
||||
--------------------
|
||||
|
||||
If you want don't want to type in the code and/or set up an SBT project then you can
|
||||
check out the full tutorial from the Akka GitHub repository. It is in the
|
||||
``akka-tutorials/akka-tutorial-first`` module. You can also browse it online
|
||||
`here`__, with the actual source code `here`__.
|
||||
|
||||
__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first
|
||||
__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala
|
||||
|
||||
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
|
||||
=============
|
||||
|
|
@ -71,7 +89,7 @@ To build and run the tutorial sample from the command line, you have to download
|
|||
Akka. If you prefer to use SBT to build and run the sample then you can skip this
|
||||
section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
Let's get the ``akka-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -82,47 +100,54 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ cd /Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
/Users/jboner/tools/akka-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
$ ls -1
|
||||
bin
|
||||
config
|
||||
deploy
|
||||
doc
|
||||
lib
|
||||
src
|
||||
|
||||
- In the ``bin`` directory we have scripts for starting the Akka Microkernel.
|
||||
- In the ``config`` directory we have the Akka conf files.
|
||||
- In the ``doc`` directory we have the documentation, API, doc JARs, and also
|
||||
the source files for the tutorials.
|
||||
- In the ``deploy`` directory we can place applications to be run with the microkernel.
|
||||
- In the ``doc`` directory we have the documentation, API, and doc JARs.
|
||||
- In the ``lib`` directory we have the Scala and Akka JARs.
|
||||
- In the ``src`` directory we have the source JARs for Akka.
|
||||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The
|
||||
core distribution has seven modules:
|
||||
modules are:
|
||||
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor`` -- Actors
|
||||
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
- ``akka-remote`` -- Remote Actors
|
||||
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
- ``akka-slf4j`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
|
||||
- ``akka-testkit`` -- Toolkit for testing Actors
|
||||
|
||||
- ``akka-kernel`` -- Akka microkernel for running a bare-bones mini application server
|
||||
|
||||
- ``akka-durable-mailboxes`` -- Durable mailboxes: file-based, MongoDB, Redis, Zookeeper
|
||||
|
||||
- ``akka-amqp`` -- AMQP integration
|
||||
|
||||
.. - ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
.. - ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
.. - ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
.. - ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing Scala
|
||||
|
|
@ -133,19 +158,19 @@ the Scala distribution. If you prefer to use SBT to build and run the sample
|
|||
then you can skip this section and jump to the next one.
|
||||
|
||||
Scala can be downloaded from http://www.scala-lang.org/downloads. Browse there
|
||||
and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip``
|
||||
and download the Scala 2.9.1 release. If you pick the ``tgz`` or ``zip``
|
||||
distribution then just unzip it where you want it installed. If you pick the
|
||||
IzPack Installer then double click on it and follow the instructions.
|
||||
|
||||
You also need to make sure that the ``scala-2.9.0/bin`` (if that is the
|
||||
You also need to make sure that the ``scala-2.9.1/bin`` (if that is the
|
||||
directory where you installed Scala) is on your ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:scala-2.9.0/bin
|
||||
$ export PATH=$PATH:scala-2.9.1/bin
|
||||
|
||||
You can test your installation by invoking scala::
|
||||
|
||||
$ scala -version
|
||||
Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL
|
||||
Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
|
||||
|
||||
Looks like we are all good. Finally let's create a source file ``Pi.scala`` for
|
||||
the tutorial and put it in the root of the Akka distribution in the ``tutorial``
|
||||
|
|
@ -199,14 +224,12 @@ files.
|
|||
Not needed in this tutorial, but if you would like to use additional Akka
|
||||
modules beyond ``akka-actor``, you can add these as ``libraryDependencies`` in
|
||||
``build.sbt``. Note that there must be a blank line between each. Here is an
|
||||
example adding ``akka-remote`` and ``akka-stm``::
|
||||
example adding ``akka-remote``::
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-actor" % "2.0-SNAPSHOT"
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-remote" % "2.0-SNAPSHOT"
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-stm" % "2.0-SNAPSHOT"
|
||||
|
||||
So, now we are all set.
|
||||
|
||||
SBT itself needs a whole bunch of dependencies but our project will only need
|
||||
|
|
@ -221,7 +244,7 @@ Now it's about time to start hacking.
|
|||
We start by creating a ``Pi.scala`` file and adding these import statements at
|
||||
the top of the file:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#imports
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#imports
|
||||
|
||||
If you are using SBT in this tutorial then create the file in the
|
||||
``src/main/scala`` directory.
|
||||
|
|
@ -256,7 +279,7 @@ start by creating three messages as case classes. We also create a common base
|
|||
trait for our messages (that we define as being ``sealed`` in order to prevent
|
||||
creating messages outside our control):
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#messages
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#messages
|
||||
|
||||
|
||||
Creating the worker
|
||||
|
|
@ -267,67 +290,34 @@ trait and defining the ``receive`` method. The ``receive`` method defines our
|
|||
message handler. We expect it to be able to handle the ``Work`` message so we
|
||||
need to add a handler for this message:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#worker
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#worker
|
||||
:exclude: calculatePiFor
|
||||
|
||||
As you can see we have now created an ``Actor`` with a ``receive`` method as a
|
||||
handler for the ``Work`` message. In this handler we invoke the
|
||||
``calculatePiFor(..)`` method, wrap the result in a ``Result`` message and send
|
||||
it back to the original sender using ``self.reply``. In Akka the sender
|
||||
reference is implicitly passed along with the message so that the receiver can
|
||||
always reply or store away the sender reference for future use.
|
||||
it back asynchronously to the original sender using the ``sender`` reference.
|
||||
In Akka the sender reference is implicitly passed along with the message so that
|
||||
the receiver can always reply or store away the sender reference for future use.
|
||||
|
||||
The only thing missing in our ``Worker`` actor is the implementation on the
|
||||
``calculatePiFor(..)`` method. While there are many ways we can implement this
|
||||
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
||||
style using a for comprehension and an accumulator:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#calculatePiFor
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#calculatePiFor
|
||||
|
||||
|
||||
Creating the master
|
||||
===================
|
||||
|
||||
The master actor is a little bit more involved. In its constructor we need to
|
||||
create the workers (the ``Worker`` actors) and start them. We will also wrap
|
||||
them in a load-balancing router to make it easier to spread out the work evenly
|
||||
between the workers. Let's do that first:
|
||||
The master actor is a little bit more involved. In its constructor we create a round-robin router
|
||||
to make it easier to spread out the work evenly between the workers. Let's do that first:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#create-workers
|
||||
|
||||
As you can see we are using the ``actorOf`` factory method to create actors,
|
||||
this method returns as an ``ActorRef`` which is a reference to our newly created
|
||||
actor. This method is available in the ``Actor`` object but is usually
|
||||
imported::
|
||||
|
||||
import akka.actor.Actor.actorOf
|
||||
|
||||
There are two versions of ``actorOf``; one of them taking a actor type and the
|
||||
other one an instance of an actor. The former one (``actorOf(Props[MyActor]``) is used
|
||||
when the actor class has a no-argument constructor while the second one
|
||||
(``actorOf(Props(new MyActor(..))``) is used when the actor class has a constructor
|
||||
that takes arguments. This is the only way to create an instance of an Actor and
|
||||
the ``actorOf`` method ensures this. The latter version is using call-by-name
|
||||
and lazily creates the actor within the scope of the ``actorOf`` method. The
|
||||
``actorOf`` method instantiates the actor and returns, not an instance to the
|
||||
actor, but an instance to an ``ActorRef``. This reference is the handle through
|
||||
which you communicate with the actor. It is immutable, serializable and
|
||||
location-aware meaning that it "remembers" its original actor even if it is sent
|
||||
to other nodes across the network and can be seen as the equivalent to the
|
||||
Erlang actor's PID.
|
||||
|
||||
The actor's life-cycle is:
|
||||
|
||||
- Created & Started -- ``Actor.actorOf(Props[MyActor])`` -- can receive messages
|
||||
- Stopped -- ``actorRef.stop()`` -- can **not** receive messages
|
||||
|
||||
Once the actor has been stopped it is dead and can not be started again.
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#create-router
|
||||
|
||||
Now we have a router that is representing all our workers in a single
|
||||
abstraction. If you paid attention to the code above, you saw that we were using
|
||||
the ``nrOfWorkers`` variable. This variable and others we have to pass to the
|
||||
``Master`` actor in its constructor. So now let's create the master actor. We
|
||||
have to pass in three integer variables:
|
||||
abstraction. So now let's create the master actor. We pass it three integer variables:
|
||||
|
||||
- ``nrOfWorkers`` -- defining how many workers we should start up
|
||||
- ``nrOfMessages`` -- defining how many number chunks to send out to the workers
|
||||
|
|
@ -335,7 +325,7 @@ have to pass in three integer variables:
|
|||
|
||||
Here is the master actor:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#master
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master
|
||||
:exclude: handle-messages
|
||||
|
||||
A couple of things are worth explaining further.
|
||||
|
|
@ -343,17 +333,16 @@ A couple of things are worth explaining further.
|
|||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the
|
||||
``Master`` actor. This latch is only used for plumbing (in this specific
|
||||
tutorial), to have a simple way of letting the outside world knowing when the
|
||||
master can deliver the result and shut down. In more idiomatic Akka code, as we
|
||||
will see in part two of this tutorial series, we would not use a latch but other
|
||||
abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the
|
||||
same thing in a non-blocking way. But for simplicity let's stick to a
|
||||
``CountDownLatch`` for now.
|
||||
master can deliver the result and shut down. In more idiomatic Akka code
|
||||
we would not use a latch but other abstractions and functions like ``Future``
|
||||
and ``?`` to achieve the same thing in a non-blocking way.
|
||||
But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and
|
||||
``postStop``. In the ``preStart`` callback we are recording the time when the
|
||||
actor is started and in the ``postStop`` callback we are printing out the result
|
||||
(the approximation of Pi) and the time it took to calculate it. In this call we
|
||||
also invoke ``latch.countDown`` to tell the outside world that we are done.
|
||||
also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
|
||||
But we are not done yet. We are missing the message handler for the ``Master``
|
||||
actor. This message handler needs to be able to react to two different messages:
|
||||
|
|
@ -361,22 +350,19 @@ actor. This message handler needs to be able to react to two different messages:
|
|||
- ``Calculate`` -- which should start the calculation
|
||||
- ``Result`` -- which should aggregate the different results
|
||||
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` actors and
|
||||
after doing that it also sends a ``Broadcast(PoisonPill)`` message to the
|
||||
router, which will send out the ``PoisonPill`` message to all the actors it is
|
||||
representing (in our case all the ``Worker`` actors). ``PoisonPill`` is a
|
||||
special kind of message that tells the receiver to shut itself down using the
|
||||
normal shutdown method; ``self.stop``. We also send a ``PoisonPill`` to the
|
||||
router itself (since it's also an actor that we want to shut down).
|
||||
The ``Calculate`` handler is sending out work to all the ``Worker`` via its router.
|
||||
|
||||
The ``Result`` handler is simpler, here we get the value from the ``Result``
|
||||
message and aggregate it to our ``pi`` member variable. We also keep track of
|
||||
how many results we have received back, and if that matches the number of tasks
|
||||
sent out, the ``Master`` actor considers itself done and shuts down.
|
||||
The ``Result`` handler gets the value from the ``Result`` message and aggregates it to
|
||||
our ``pi`` member variable. We also keep track of how many results we have received back,
|
||||
and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and
|
||||
invokes the ``self.stop()`` method to stop itself *and* all its supervised actors.
|
||||
In this case it has one supervised actor, the router, and this in turn has ``nrOfWorkers`` supervised actors.
|
||||
All of them will be stopped automatically as the invocation of any supervisor's ``stop`` method
|
||||
will propagate down to all its supervised 'children'.
|
||||
|
||||
Let's capture this in code:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#master-receive
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
|
||||
|
||||
|
||||
Bootstrap the calculation
|
||||
|
|
@ -391,29 +377,35 @@ The ``Pi`` object is a perfect container module for our actors and messages, so
|
|||
let's put them all there. We also create a method ``calculate`` in which we
|
||||
start up the ``Master`` actor and wait for it to finish:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala#app
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
|
||||
:exclude: actors-and-messages
|
||||
|
||||
As you can see the *calculate* method above it creates an ActorSystem and this is the Akka container which
|
||||
will contain all actors created in that "context". An example of how to create actors in the container
|
||||
is the *'system.actorOf(...)'* line in the calculate method. In this case we create a top level actor.
|
||||
If you instead where in an actor context, i.e. inside an actor creating other actors, you should use
|
||||
*context.actorOf(...)*. This is illustrated in the Master code above.
|
||||
|
||||
That's it. Now we are done.
|
||||
|
||||
But before we package it up and run it, let's take a look at the full code now,
|
||||
with package declaration, imports and all:
|
||||
|
||||
.. includecode:: code/tutorials/first/Pi.scala
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala
|
||||
|
||||
|
||||
Run it as a command line application
|
||||
====================================
|
||||
|
||||
If you have not typed in (or copied) the code for the tutorial as
|
||||
``$AKKA_HOME/tutorial/Pi.scala`` then now is the time. When that's done open up
|
||||
a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
||||
If you have not typed in (or copied) the code for the tutorial as in
|
||||
``$AKKA_HOME/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala`` then now is the time.
|
||||
When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
||||
|
||||
First we need to compile the source file. That is done with Scala's compiler
|
||||
``scalac``. Our application depends on the ``akka-actor-2.0-SNAPSHOT.jar`` JAR
|
||||
file, so let's add that to the compiler classpath when we compile the source::
|
||||
|
||||
$ scalac -cp lib/akka/akka-actor-2.0-SNAPSHOT.jar tutorial/Pi.scala
|
||||
$ scalac -cp lib/akka/akka-actor-2.0-SNAPSHOT.jar Pi.scala
|
||||
|
||||
When we have compiled the source file we are ready to run the application. This
|
||||
is done with ``java`` but yet again we need to add the
|
||||
|
|
@ -426,7 +418,7 @@ compiled ourselves::
|
|||
akka.tutorial.first.scala.Pi
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 858 millis
|
||||
Calculation time: 553 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
|
|
@ -445,7 +437,7 @@ When this in done we can run our application directly inside SBT::
|
|||
> run
|
||||
...
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 942 millis
|
||||
Calculation time: 531 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ Download
|
|||
--------
|
||||
|
||||
There are several ways to download Akka. You can download the full distribution
|
||||
with microkernel, which includes all modules. You can download just the core
|
||||
distribution or just the actors distribution. Or you can use a build tool like
|
||||
with microkernel, which includes all modules. Or you can use a build tool like
|
||||
Maven or sbt to download dependencies from the Akka Maven repository.
|
||||
|
||||
Modules
|
||||
|
|
@ -54,7 +53,7 @@ Akka is very modular and has many JARs for containing different features.
|
|||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server
|
||||
|
||||
How to see the JARs dependencies of each Akka module is described in the
|
||||
:ref:`dependencies` section. Worth noting is that ``akka-actor`` has zero
|
||||
|
|
@ -69,18 +68,18 @@ http://akka.io/downloads and unzip it.
|
|||
Using a snapshot version
|
||||
------------------------
|
||||
|
||||
The Akka nightly snapshots are published to
|
||||
http://repo.typesafe.com/typesafe/maven-timestamps/ and are versioned with a
|
||||
timestamp. You need to choose a timestamped version to work with and can decide
|
||||
when to update to a newer version.
|
||||
The Akka nightly snapshots are published to http://akka.io/snapshots/ and are
|
||||
versioned with both ``SNAPSHOT`` and timestamps. You can choose a timestamped
|
||||
version to work with and can decide when to update to a newer version. The Akka
|
||||
snapshots repository is also proxied through http://repo.typesafe.com/typesafe/snapshots/
|
||||
which includes proxies for several other repositories that Akka modules depend on.
|
||||
|
||||
Microkernel
|
||||
^^^^^^^^^^^
|
||||
-----------
|
||||
|
||||
The Akka Modules distribution includes the microkernel. To run the microkernel:
|
||||
|
||||
* Set the AKKA_HOME environment variable to the root of the Akka distribution.
|
||||
* To start the kernel use the scripts in the ``bin`` directory and deploy all samples applications from ``./deploy`` dir.
|
||||
The Akka distribution includes the microkernel. To run the microkernel put your
|
||||
application jar in the ``deploy`` directory and use the scripts in the ``bin``
|
||||
directory.
|
||||
|
||||
More information is available in the documentation of the :ref:`microkernel`.
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ Using a build tool
|
|||
------------------
|
||||
|
||||
Akka can be used with build tools that support Maven repositories. The Akka
|
||||
Maven repository can be found at http://akka.io/repository/ and Typesafe provides
|
||||
Maven repository can be found at http://akka.io/realeses/ and Typesafe provides
|
||||
http://repo.typesafe.com/typesafe/releases/ that proxies several other
|
||||
repositories, including akka.io.
|
||||
|
||||
|
|
@ -121,7 +120,7 @@ Summary of the essential parts for using Akka with Maven:
|
|||
<version>2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
**Note**: for snapshot versions akka uses specific timestamped versions.
|
||||
**Note**: for snapshot versions both ``SNAPSHOT`` and timestamped versions are published.
|
||||
|
||||
|
||||
Using Akka with SBT
|
||||
|
|
@ -166,7 +165,6 @@ Build from sources
|
|||
Akka uses Git and is hosted at `Github <http://github.com>`_.
|
||||
|
||||
* Akka: clone the Akka repository from `<http://github.com/jboner/akka>`_
|
||||
* Akka Modules: clone the Akka Modules repository from `<http://github.com/jboner/akka-modules>`_
|
||||
|
||||
Continue reading the page on :ref:`building-akka`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
Examples of use-cases for Akka
|
||||
==============================
|
||||
|
||||
There is a great discussion on use-cases for Akka with some good write-ups by production users `here <http://stackoverflow.com/questions/4493001/good-use-case-for-akka/4494512#4494512>`_
|
||||
There is a great discussion on use-cases for Akka with some good write-ups by production
|
||||
users `here <http://stackoverflow.com/questions/4493001/good-use-case-for-akka/4494512#4494512>`_
|
||||
|
||||
Here are some of the areas where Akka is being deployed into production
|
||||
-----------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ Actors give 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).
|
||||
- Very lightweight event-driven processes (approximately 2.7 million actors per GB RAM).
|
||||
|
||||
See :ref:`actors-scala` and :ref:`untyped-actors-java`
|
||||
|
||||
|
|
@ -47,16 +47,11 @@ systems that self-heal.
|
|||
|
||||
See :ref:`fault-tolerance-scala` and :ref:`fault-tolerance-java`
|
||||
|
||||
Software Transactional Memory (STM)
|
||||
-----------------------------------
|
||||
|
||||
See :ref:`stm-scala` and :ref:`stm-java`
|
||||
|
||||
Transactors
|
||||
-----------
|
||||
|
||||
Transactors combine actors and STM into transactional actors. Allows you to
|
||||
compose atomic message flows with automatic retry and rollback.
|
||||
Transactors combine actors and STM (Software Transactional Memory) into transactional actors.
|
||||
It allows you to compose atomic message flows with automatic retry and rollback.
|
||||
|
||||
See :ref:`transactors-scala` and :ref:`transactors-java`
|
||||
|
||||
|
|
@ -81,7 +76,6 @@ 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.
|
||||
- As a microkernel: stand-alone kernel to drop your application into.
|
||||
|
||||
See the :ref:`deployment-scenarios` for details.
|
||||
|
|
|
|||
|
|
@ -26,10 +26,8 @@ of concurrency-paradigms, allowing for users to choose the right tool for the
|
|||
job.
|
||||
|
||||
The integration possibilities for Akka Actors are immense through the Apache
|
||||
Camel integration. We provide Software Transactional Memory concurrency control
|
||||
through the excellent Multiverse project, and have integrated that with Actors,
|
||||
creating Transactors for coordinated concurrent transactions. We have Agents and
|
||||
Dataflow concurrency as well.
|
||||
Camel integration. We have Transactors for coordinated concurrent transactions,
|
||||
as well as Agents and Dataflow concurrency.
|
||||
|
||||
|
||||
What's a good use-case for Akka?
|
||||
|
|
@ -54,14 +52,12 @@ have a commercial product called Akka Atmos which provides the following
|
|||
features:
|
||||
|
||||
#. Management through Dashboard, JMX and REST
|
||||
#. Monitoring through Dashboard, JMX and SNMP
|
||||
#. Dapper-style tracing of messages across components and remote nodes
|
||||
#. A configurable alert system
|
||||
#. Real-time statistics
|
||||
#. Very low overhead monitoring agents (should always be on in production)
|
||||
#. Consolidation of statistics and logging information to a single node
|
||||
#. Data analysis through Hadoop
|
||||
#. Storage of statistics data for later processing
|
||||
#. Provisioning and rolling upgrades through a dashboard
|
||||
#. Provisioning and rolling upgrades
|
||||
|
||||
Read more `here <http://typesafe.com/products/typesafe-subscription>`_.
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ package akka.docs.actor
|
|||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class UntypedActorTest extends UntypedActorTestBase with JUnitSuite
|
||||
class SchedulerDocTest extends SchedulerDocTestBase with JUnitSuite
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package akka.docs.actor;
|
||||
|
||||
//#imports1
|
||||
import akka.actor.Props;
|
||||
import akka.util.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//#imports1
|
||||
|
||||
//#imports2
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
import akka.actor.Cancellable;
|
||||
|
||||
//#imports2
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.testkit.AkkaSpec;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SchedulerDocTestBase {
|
||||
|
||||
ActorSystem system;
|
||||
ActorRef testActor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
system = ActorSystem.create("MySystem", AkkaSpec.testConf());
|
||||
testActor = system.actorOf(new Props().withCreator(MyUntypedActor.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
system.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scheduleOneOffTask() {
|
||||
//#schedule-one-off-message
|
||||
//Schedules to send the "foo"-message to the testActor after 50ms
|
||||
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS), testActor, "foo");
|
||||
//#schedule-one-off-message
|
||||
|
||||
//#schedule-one-off-thunk
|
||||
//Schedules a Runnable to be executed (send the current time) to the testActor after 50ms
|
||||
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
testActor.tell(System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
//#schedule-one-off-thunk
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scheduleRecurringTask() {
|
||||
//#schedule-recurring
|
||||
ActorRef tickActor = system.actorOf(new Props().withCreator(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new UntypedActor() {
|
||||
public void onReceive(Object message) {
|
||||
if (message.equals("Tick")) {
|
||||
// Do someting
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
//This will schedule to send the Tick-message
|
||||
//to the tickActor after 0ms repeating every 50ms
|
||||
Cancellable cancellable = system.scheduler().schedule(Duration.Zero(), Duration.create(50, TimeUnit.MILLISECONDS),
|
||||
tickActor, "Tick");
|
||||
|
||||
//This cancels further Ticks to be sent
|
||||
cancellable.cancel();
|
||||
//#schedule-recurring
|
||||
system.stop(tickActor);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package akka.docs.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class UntypedActorDocTest extends UntypedActorDocTestBase with JUnitSuite
|
||||
|
|
@ -4,24 +4,21 @@ package akka.docs.actor;
|
|||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
|
||||
//#imports
|
||||
|
||||
//#import-future
|
||||
import akka.dispatch.Future;
|
||||
import akka.dispatch.Await;
|
||||
import akka.util.Duration;
|
||||
|
||||
import akka.util.Timeout;
|
||||
//#import-future
|
||||
|
||||
//#import-actors
|
||||
import static akka.actor.Actors.*;
|
||||
|
||||
//#import-actors
|
||||
|
||||
//#import-procedure
|
||||
import akka.japi.Procedure;
|
||||
|
||||
//#import-procedure
|
||||
|
||||
import akka.actor.Props;
|
||||
|
|
@ -36,7 +33,26 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class UntypedActorTestBase {
|
||||
public class UntypedActorDocTestBase {
|
||||
|
||||
@Test
|
||||
public void createProps() {
|
||||
//#creating-props-config
|
||||
Props props1 = new Props();
|
||||
Props props2 = new Props(MyUntypedActor.class);
|
||||
Props props3 = new Props(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new MyUntypedActor();
|
||||
}
|
||||
});
|
||||
Props props4 = props1.withCreator(new UntypedActorFactory() {
|
||||
public UntypedActor create() {
|
||||
return new MyUntypedActor();
|
||||
}
|
||||
});
|
||||
Props props5 = props4.withTimeout(new Timeout(1000));
|
||||
//#creating-props-config
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemActorOf() {
|
||||
|
|
@ -166,6 +182,8 @@ public class UntypedActorTestBase {
|
|||
}
|
||||
|
||||
public void preRestart(Throwable reason, Option<Object> message) {
|
||||
for (ActorRef each : getContext().getChildren())
|
||||
getContext().stop(each);
|
||||
postStop();
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ import com.typesafe.config.ConfigFactory;
|
|||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.docs.actor.MyUntypedActor;
|
||||
import akka.docs.actor.UntypedActorTestBase.MyActor;
|
||||
import akka.docs.actor.UntypedActorDocTestBase.MyActor;
|
||||
import akka.testkit.AkkaSpec;
|
||||
|
||||
public class DispatcherDocTestBase {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package akka.docs.extension
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class ExtensionDocTest extends ExtensionDocTestBase with JUnitSuite
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package akka.docs.extension;
|
||||
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
//#imports
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ExtensionDocTestBase {
|
||||
|
||||
//#extension
|
||||
public static class CountExtensionImpl implements Extension {
|
||||
//Since this Extension is a shared instance
|
||||
// per ActorSystem we need to be threadsafe
|
||||
private final AtomicLong counter = new AtomicLong(0);
|
||||
|
||||
//This is the operation this Extension provides
|
||||
public long increment() {
|
||||
return counter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
//#extension
|
||||
|
||||
//#extensionid
|
||||
static class CountExtensionId extends AbstractExtensionId<CountExtensionImpl> {
|
||||
//This method will be called by Akka
|
||||
// to instantiate our Extension
|
||||
public CountExtensionImpl createExtension(ActorSystemImpl i) {
|
||||
return new CountExtensionImpl();
|
||||
}
|
||||
}
|
||||
|
||||
//This will be the identifier of our CountExtension
|
||||
public final static CountExtensionId CountExtension = new CountExtensionId();
|
||||
//#extensionid
|
||||
|
||||
//#extensionid-provider
|
||||
static class CountExtensionIdProvider implements ExtensionIdProvider {
|
||||
public CountExtensionId lookup() {
|
||||
return CountExtension; //The public static final
|
||||
}
|
||||
}
|
||||
//#extensionid-provider
|
||||
|
||||
//#extension-usage-actor
|
||||
static class MyActor extends UntypedActor {
|
||||
public void onReceive(Object msg) {
|
||||
CountExtension.get(getContext().system()).increment();
|
||||
}
|
||||
}
|
||||
//#extension-usage-actor
|
||||
|
||||
|
||||
@Test public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() {
|
||||
final ActorSystem system = null;
|
||||
try {
|
||||
//#extension-usage
|
||||
CountExtension.get(system).increment();
|
||||
//#extension-usage
|
||||
} catch(Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -154,8 +154,9 @@ if not specified otherwise.
|
|||
akka {
|
||||
actor {
|
||||
default-dispatcher {
|
||||
task-queue-size = 1000 # If negative (or zero) then an unbounded mailbox is used (default)
|
||||
# If negative (or zero) then an unbounded mailbox is used (default)
|
||||
# If positive then a bounded mailbox is used and the capacity is set to the number specified
|
||||
task-queue-size = 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
63
akka-docs/java/extending-akka.rst
Normal file
63
akka-docs/java/extending-akka.rst
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
.. _extending-akka:
|
||||
|
||||
Akka Extensions
|
||||
===============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
If you want to add features to Akka, there is a very elegant, but powerful mechanism for doing so.
|
||||
It's called Akka Extensions and is comprised of 2 basic components: an ``Extension`` and an ``ExtensionId``.
|
||||
|
||||
Extensions will only be loaded once per ``ActorSystem``, which will be managed by Akka.
|
||||
You can choose to have your Extension loaded on-demand or at ``ActorSystem`` creation time through the Akka configuration.
|
||||
Details on how to make that happens are below, in the "Loading from Configuration" section.
|
||||
|
||||
.. warning::
|
||||
|
||||
Since an extension is a way to hook into Akka itself, the implementor of the extension needs to
|
||||
ensure the thread safety of his/her extension.
|
||||
|
||||
|
||||
Building an Extension
|
||||
---------------------
|
||||
|
||||
So let's create a sample extension that just lets us count the number of times something has happened.
|
||||
|
||||
First, we define what our ``Extension`` should do:
|
||||
|
||||
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
|
||||
:include: imports,extension
|
||||
|
||||
Then we need to create an ``ExtensionId`` for our extension so we can grab ahold of it.
|
||||
|
||||
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
|
||||
:include: imports,extensionid
|
||||
|
||||
Wicked! Now all we need to do is to actually use it:
|
||||
|
||||
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
|
||||
:include: extension-usage
|
||||
|
||||
Or from inside of an Akka Actor:
|
||||
|
||||
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
|
||||
:include: extension-usage-actor
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
Loading from Configuration
|
||||
--------------------------
|
||||
|
||||
To be able to load extensions from your Akka configuration you must add FQCNs of implementations of either ``ExtensionId`` or ``ExtensionIdProvider``
|
||||
in the "akka.extensions" section of the config you provide to your ``ActorSystem``.
|
||||
|
||||
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
|
||||
:include: extensionid-provider
|
||||
|
||||
Applicability
|
||||
-------------
|
||||
|
||||
The sky is the limit!
|
||||
By the way, did you know that Akka's ``Typed Actors``, ``Serialization`` and other features are implemented as Akka Extensions?
|
||||
|
|
@ -9,11 +9,12 @@ Java API
|
|||
untyped-actors
|
||||
typed-actors
|
||||
logging
|
||||
scheduler
|
||||
futures
|
||||
dataflow
|
||||
stm
|
||||
transactors
|
||||
fault-tolerance
|
||||
dispatchers
|
||||
routing
|
||||
guice-integration
|
||||
extending-akka
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ Here you can also define the log level.
|
|||
akka {
|
||||
# Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT)
|
||||
event-handlers = ["akka.event.Logging$DefaultLogger"]
|
||||
loglevel = "DEBUG" # Options: ERROR, WARNING, INFO, DEBUG
|
||||
# Options: ERROR, WARNING, INFO, DEBUG
|
||||
loglevel = "DEBUG"
|
||||
}
|
||||
|
||||
The default one logs to STDOUT and is registered by default. It is not intended to be used for production. There is also an :ref:`slf4j-java`
|
||||
|
|
|
|||
53
akka-docs/java/scheduler.rst
Normal file
53
akka-docs/java/scheduler.rst
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
.. _scheduler-java:
|
||||
|
||||
##################
|
||||
Scheduler (Java)
|
||||
##################
|
||||
|
||||
Sometimes the need for making things happen in the future arises, and where do you go look then?
|
||||
Look no further than ``ActorSystem``! There you find the :meth:`scheduler` method that returns an instance
|
||||
of akka.actor.Scheduler, this instance is unique per ActorSystem and is used internally for scheduling things
|
||||
to happen at specific points in time. Please note that the scheduled tasks are executed by the default
|
||||
``MessageDispatcher`` of the ``ActorSystem``.
|
||||
|
||||
You can schedule sending of messages to actors and execution of tasks (functions or Runnable).
|
||||
You will get a ``Cancellable`` back that you can call :meth:`cancel` on to cancel the execution of the
|
||||
scheduled operation.
|
||||
|
||||
Some examples
|
||||
-------------
|
||||
|
||||
.. includecode:: code/akka/docs/actor/SchedulerDocTestBase.java
|
||||
:include: imports1,schedule-one-off-message
|
||||
|
||||
.. includecode:: code/akka/docs/actor/SchedulerDocTestBase.java
|
||||
:include: schedule-one-off-thunk
|
||||
|
||||
.. includecode:: code/akka/docs/actor/SchedulerDocTestBase.java
|
||||
:include: imports1,imports2,schedule-recurring
|
||||
|
||||
From ``akka.actor.ActorSystem``
|
||||
-------------------------------
|
||||
|
||||
.. includecode:: ../../akka-actor/src/main/scala/akka/actor/ActorSystem.scala
|
||||
:include: scheduler
|
||||
|
||||
|
||||
The Scheduler interface
|
||||
-----------------------
|
||||
|
||||
.. includecode:: ../../akka-actor/src/main/scala/akka/actor/Scheduler.scala
|
||||
:include: scheduler
|
||||
|
||||
The Cancellable interface
|
||||
-------------------------
|
||||
|
||||
This allows you to ``cancel`` something that has been scheduled for execution.
|
||||
|
||||
.. warning::
|
||||
This does not abort the execution of the task, if it had already been started.
|
||||
|
||||
.. includecode:: ../../akka-actor/src/main/scala/akka/actor/Scheduler.scala
|
||||
:include: cancellable
|
||||
|
||||
6
akka-docs/java/transactors.rst
Normal file
6
akka-docs/java/transactors.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. _transactors-java:
|
||||
|
||||
Transactors (Java)
|
||||
==================
|
||||
|
||||
The Akka Transactors module has not been migrated to Akka 2.0-SNAPSHOT yet.
|
||||
|
|
@ -39,10 +39,27 @@ Here is an example:
|
|||
|
||||
.. includecode:: code/akka/docs/actor/MyUntypedActor.java#my-untyped-actor
|
||||
|
||||
Props
|
||||
-----
|
||||
|
||||
``Props`` is a configuration class to specify options for the creation
|
||||
of actors. Here are some examples on how to create a ``Props`` instance.
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#creating-props-config
|
||||
|
||||
|
||||
Creating Actors with Props
|
||||
--------------------------
|
||||
|
||||
Actors are created by passing in a ``Props`` instance into the ``actorOf`` factory method.
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#creating-props
|
||||
|
||||
|
||||
Creating Actors with default constructor
|
||||
----------------------------------------
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||
:include: imports,system-actorOf
|
||||
|
||||
The call to :meth:`actorOf` returns an instance of ``ActorRef``. This is a handle to
|
||||
|
|
@ -76,26 +93,16 @@ add initialization code for the actor.
|
|||
Creating Actors with non-default constructor
|
||||
--------------------------------------------
|
||||
|
||||
If your UntypedActor has a constructor that takes parameters then you can't create it using 'actorOf(clazz)'.
|
||||
Instead you can use a variant of ``actorOf`` that takes an instance of an 'UntypedActorFactory'
|
||||
in which you can create the Actor in any way you like. If you use this method then you to make sure that
|
||||
no one can get a reference to the actor instance. If they can get a reference it then they can
|
||||
touch state directly in bypass the whole actor dispatching mechanism and create race conditions
|
||||
which can lead to corrupt data.
|
||||
If your UntypedActor has a constructor that takes parameters then you can't create it using
|
||||
'actorOf(new Props(clazz))'. Then you can instead pass in 'new Props(new UntypedActorFactory() {..})'
|
||||
in which you can create the Actor in any way you like.
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#creating-constructor
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#creating-constructor
|
||||
|
||||
This way of creating the Actor is also great for integrating with Dependency Injection (DI) frameworks like Guice or Spring.
|
||||
|
||||
Creating Actors with Props
|
||||
--------------------------
|
||||
|
||||
``Props`` is a configuration object to specify additional things for the actor to
|
||||
be created, such as the ``MessageDispatcher``.
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#creating-props
|
||||
This way of creating the Actor is also great for integrating with Dependency Injection
|
||||
(DI) frameworks like Guice or Spring.
|
||||
|
||||
|
||||
UntypedActor API
|
||||
|
|
@ -119,7 +126,7 @@ In addition, it offers:
|
|||
The remaining visible methods are user-overridable life-cycle hooks which are
|
||||
described in the following:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#lifecycle-callbacks
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#lifecycle-callbacks
|
||||
|
||||
The implementations shown above are the defaults provided by the :class:`UntypedActor`
|
||||
class.
|
||||
|
|
@ -152,7 +159,7 @@ processing a message. This restart involves the hooks mentioned above:
|
|||
message, e.g. when a supervisor does not trap the exception and is restarted
|
||||
in turn by its supervisor. This method is the best place for cleaning up,
|
||||
preparing hand-over to the fresh actor instance, etc.
|
||||
By default it calls :meth:`postStop`.
|
||||
By default it stops all children and calls :meth:`postStop`.
|
||||
2. The initial factory from the ``actorOf`` call is used
|
||||
to produce the fresh instance.
|
||||
3. The new actor’s :meth:`postRestart` method is invoked with the exception
|
||||
|
|
@ -250,7 +257,7 @@ To complete the future with an exception you need send a Failure message to the
|
|||
This is not done automatically when an actor throws an exception while processing a
|
||||
message.
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java#reply-exception
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#reply-exception
|
||||
|
||||
If the actor does not complete the future, it will expire after the timeout period,
|
||||
specified as parameter to the ``ask`` method.
|
||||
|
|
@ -278,7 +285,7 @@ even if that entails waiting for it (but keep in mind that waiting inside an
|
|||
actor is prone to dead-locks, e.g. if obtaining the result depends on
|
||||
processing another message on this actor).
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||
:include: import-future,using-ask
|
||||
|
||||
Forward message
|
||||
|
|
@ -340,13 +347,11 @@ message.
|
|||
Stopping actors
|
||||
===============
|
||||
|
||||
Actors are stopped by invoking the ``stop`` method of the ``ActorRef``.
|
||||
The actual termination of the actor is performed asynchronously, i.e.
|
||||
``stop`` may return before the actor is stopped.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
actor.stop();
|
||||
Actors are stopped by invoking the :meth:`stop` method of a ``ActorRefFactory``,
|
||||
i.e. ``ActorContext`` or ``ActorSystem``. Typically the context is used for stopping
|
||||
child actors and the system for stopping top level actors. The actual termination of
|
||||
the actor is performed asynchronously, i.e. :meth:`stop` may return before the actor is
|
||||
stopped.
|
||||
|
||||
Processing of the current message, if any, will continue before the actor is stopped,
|
||||
but additional messages in the mailbox will not be processed. By default these
|
||||
|
|
@ -381,7 +386,7 @@ If the ``PoisonPill`` was sent with ``ask``, the ``Future`` will be completed wi
|
|||
|
||||
Use it like this:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||
:include: import-actors,poison-pill
|
||||
|
||||
.. _UntypedActor.HotSwap:
|
||||
|
|
@ -402,7 +407,7 @@ The hotswapped code is kept in a Stack which can be pushed and popped.
|
|||
|
||||
To hotswap the Actor using ``getContext().become``:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||
:include: import-procedure,hot-swap-actor
|
||||
|
||||
The ``become`` method is useful for many different things, such as to implement
|
||||
|
|
@ -432,7 +437,7 @@ through regular supervisor semantics.
|
|||
|
||||
Use it like this:
|
||||
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorTestBase.java
|
||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||
:include: import-actors,kill
|
||||
|
||||
Actors and exceptions
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,4 +5,32 @@
|
|||
Microkernel
|
||||
#############
|
||||
|
||||
The Akka Spring module has not been migrated to Akka 2.0-SNAPSHOT yet.
|
||||
The Akka Microkernel is included in the Akka download found at `downloads`_.
|
||||
|
||||
.. _downloads: http://akka.io/downloads
|
||||
|
||||
To run an application with the microkernel you need to create a Bootable class
|
||||
that handles the startup and shutdown the application. An example is included below.
|
||||
|
||||
Put your application jar in the ``deploy`` directory to have it automatically
|
||||
loaded.
|
||||
|
||||
To start the kernel use the scripts in the ``bin`` directory, passing the boot
|
||||
classes for your application.
|
||||
|
||||
There is a simple example of an application setup for running with the
|
||||
microkernel included in the akka download. This can be run with the following
|
||||
command (on a unix-based system):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
bin/akka sample.kernel.hello.HelloKernel
|
||||
|
||||
Use Ctrl-C to interrupt and exit the microkernel.
|
||||
|
||||
On a Windows machine you can also use the bin/akka.bat script.
|
||||
|
||||
The code for the Hello Kernel example (see the HelloKernel class for an example
|
||||
of creating a Bootable):
|
||||
|
||||
.. includecode:: ../../akka-samples/akka-sample-hello-kernel/src/main/scala/sample/kernel/hello/HelloKernel.scala
|
||||
|
|
|
|||
|
|
@ -27,41 +27,44 @@ Akka uses Git and is hosted at `Github <http://github.com>`_.
|
|||
* Akka: clone the Akka repository from `<http://github.com/jboner/akka>`_
|
||||
|
||||
|
||||
`Maven Repository <http://akka.io/repository/>`_
|
||||
================================================
|
||||
`Releases Repository <http://akka.io/releases/>`_
|
||||
=================================================
|
||||
|
||||
The Akka Maven repository can be found at `<http://akka.io/repository>`_.
|
||||
The Akka Maven repository can be found at http://akka.io/releases/.
|
||||
|
||||
Typesafe provides `<http://repo.typesafe.com/typesafe/releases/>`_ that proxies several other repositories, including akka.io.
|
||||
It is convenient to use the Typesafe repository, since it includes all external dependencies of Akka.
|
||||
It is a "best-effort" service, and if it is unavailable you may need to use the underlying repositories
|
||||
directly.
|
||||
Typesafe provides http://repo.typesafe.com/typesafe/releases/ that proxies
|
||||
several other repositories, including akka.io. It is convenient to use the
|
||||
Typesafe repository, since it includes all external dependencies of Akka. It is
|
||||
a "best-effort" service, and if it is unavailable you may need to use the
|
||||
underlying repositories directly.
|
||||
|
||||
* http://akka.io/repository
|
||||
* http://repository.codehaus.org
|
||||
* http://akka.io/releases/
|
||||
* http://repository.codehaus.org/
|
||||
* http://guiceyfruit.googlecode.com/svn/repo/releases/
|
||||
* http://repository.jboss.org/nexus/content/groups/public/
|
||||
* http://download.java.net/maven/2
|
||||
* http://oss.sonatype.org/content/repositories/releases
|
||||
* http://download.java.net/maven/glassfish
|
||||
* http://databinder.net/repo
|
||||
* http://download.java.net/maven/2/
|
||||
* http://oss.sonatype.org/content/repositories/releases/
|
||||
* http://download.java.net/maven/glassfish/
|
||||
* http://databinder.net/repo/
|
||||
|
||||
SNAPSHOT Versions
|
||||
=================
|
||||
|
||||
Nightly builds are available in `<http://repo.typesafe.com/typesafe/akka-snapshots/>`_ repository as
|
||||
timestamped snapshot versions. Pick a timestamp from
|
||||
`<http://repo.typesafe.com/typesafe/akka-snapshots/com/typesafe/akka/akka-actor/>`_.
|
||||
`Snapshots Repository <http://akka.io/snapshots/>`_
|
||||
===================================================
|
||||
|
||||
Nightly builds are available in http://akka.io/snapshots/ and proxied through
|
||||
http://repo.typesafe.com/typesafe/snapshots/ as both ``SNAPSHOT`` and
|
||||
timestamped versions.
|
||||
|
||||
For timestamped versions, pick a timestamp from
|
||||
http://repo.typesafe.com/typesafe/akka-snapshots/com/typesafe/akka/akka-actor/.
|
||||
All Akka modules that belong to the same build have the same timestamp.
|
||||
|
||||
Make sure that you add the repository to the sbt resolvers or maven repositories::
|
||||
|
||||
resolvers += "Typesafe Timestamp Repo" at "http://repo.typesafe.com/typesafe/akka-snapshots/"
|
||||
resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/"
|
||||
|
||||
Define the library dependencies with the timestamp as version::
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-actor" % "2.0-20111118-000627"
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-remote" % "2.0-20111118-000627"
|
||||
Define the library dependencies with the timestamp as version. For example::
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-actor" % "2.0-20111215-000549"
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" % "akka-remote" % "2.0-20111215-000549"
|
||||
|
|
|
|||
|
|
@ -1,447 +0,0 @@
|
|||
Migration Guide 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**
|
||||
|
||||
**Scala**
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
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:**
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*; // <-- The important part
|
||||
|
||||
actorOf();
|
||||
remote().actorOf();
|
||||
registry().actorsFor("foo");
|
||||
|
||||
***akka.actor.Actor now is the API for Scala to interact with Actors, Remoting and ActorRegistry:***
|
||||
|
||||
**Scala**
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor._ // <-- The important part
|
||||
|
||||
actorOf().method
|
||||
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(Props[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-block:: scala
|
||||
|
||||
import akka.config.Supervision._
|
||||
|
||||
self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 3, 5000)
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.Supervision.*;
|
||||
|
||||
getContext().setFaultHandler(new OneForOneStrategy(new Class[] { Exception.class },50,1000))
|
||||
|
||||
**RestartStrategy, AllForOne, OneForOne** have been replaced with **AllForOneStrategy** and **OneForOneStrategy** in **se.scalablesolutions.akka.config.Supervision**
|
||||
|
||||
**Scala**
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.config.Supervision._
|
||||
SupervisorConfig(
|
||||
OneForOneStrategy(List(classOf[Exception]), 3, 5000),
|
||||
Supervise(pingpong1,Permanent) :: Nil
|
||||
)
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.Supervision.*;
|
||||
|
||||
new SupervisorConfig(
|
||||
new OneForOneStrategy(new Class[] { Exception.class },50,1000),
|
||||
new Server[] { new Supervise(pingpong1, permanent()) }
|
||||
)
|
||||
|
||||
***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:
|
||||
|
||||
**Scala**
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
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:
|
||||
|
||||
**Scala**
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.stm.local._
|
||||
|
||||
this is now just:
|
||||
|
||||
**Scala**
|
||||
|
||||
.. 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-block:: scala
|
||||
|
||||
import se.scalablesolutions.akka.camel.CamelServiceManager._
|
||||
import se.scalablesolutions.akka.camel.CamelService
|
||||
|
||||
val o: Option[CamelService] = service
|
||||
val s: CamelService = mandatoryService
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: 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();
|
||||
|
||||
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-block:: 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
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: 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();
|
||||
|
||||
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-block:: scala
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import se.scalablesolutions.akka.camel.CamelServiceManager._
|
||||
|
||||
val s = startCamelService
|
||||
val actor = Actor.actorOf(Props[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
|
||||
|
||||
**Java**
|
||||
|
||||
.. code-block:: 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();
|
||||
|
||||
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-block:: 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>
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
Migration Guide 0.8.x to 0.9.x
|
||||
==============================
|
||||
|
||||
**This document describes between the 0.8.x and the 0.9 release.**
|
||||
|
||||
Background for the new ActorRef
|
||||
-------------------------------
|
||||
|
||||
In the work towards 0.9 release we have now done a major change to how Actors are created. In short we have separated identity and value, created an 'ActorRef' that holds the actual Actor instance. This allows us to do many great things such as for example:
|
||||
|
||||
* Create serializable, immutable, network-aware Actor references that can be freely shared across the network. They "remember" their origin and will always work as expected.
|
||||
* Not only kill and restart the same supervised Actor instance when it has crashed (as we do now), but dereference it, throw it away and make it eligible for garbage collection.
|
||||
* etc. much more
|
||||
|
||||
These work very much like the 'PID' (process id) in Erlang.
|
||||
|
||||
These changes means that there is no difference in defining Actors. You still use the old Actor trait, all methods are there etc. But you can't just new this Actor up and send messages to it since all its public API methods are gone. They now reside in a new class; 'ActorRef' and use need to use instances of this class to interact with the Actor (sending messages etc.).
|
||||
|
||||
Here is a short migration guide with the things that you have to change. It is a big conceptual change but in practice you don't have to change much.
|
||||
|
||||
|
||||
|
||||
Creating Actors with default constructor
|
||||
----------------------------------------
|
||||
|
||||
From:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val a = new MyActor
|
||||
a ! msg
|
||||
|
||||
To:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import Actor._
|
||||
val a = actorOf(Props[MyActor]
|
||||
a ! msg
|
||||
|
||||
You can also start it in the same statement:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val a = actorOf(Props[MyActor]
|
||||
|
||||
Creating Actors with non-default constructor
|
||||
--------------------------------------------
|
||||
|
||||
From:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val a = new MyActor(..)
|
||||
a ! msg
|
||||
|
||||
To:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import Actor._
|
||||
val a = actorOf(Props(new MyActor(..))
|
||||
a ! msg
|
||||
|
||||
Use of 'self' ActorRef API
|
||||
--------------------------
|
||||
|
||||
Where you have used 'this' to refer to the Actor from within itself now use 'self':
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
self ! MessageToMe
|
||||
|
||||
Now the Actor trait only has the callbacks you can implement:
|
||||
* receive
|
||||
* postRestart/preRestart
|
||||
* init/shutdown
|
||||
|
||||
It has no state at all.
|
||||
|
||||
All API has been moved to ActorRef. The Actor is given its ActorRef through the 'self' member variable.
|
||||
Here you find functions like:
|
||||
* !, !!, !!! and forward
|
||||
* link, unlink, startLink, spawnLink etc
|
||||
* makeTransactional, makeRemote etc.
|
||||
* start, stop
|
||||
* etc.
|
||||
|
||||
Here you also find fields like
|
||||
* dispatcher = ...
|
||||
* id = ...
|
||||
* lifeCycle = ...
|
||||
* faultHandler = ...
|
||||
* trapExit = ...
|
||||
* etc.
|
||||
|
||||
This means that to use them you have to prefix them with 'self', like this:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
self ! Message
|
||||
|
||||
However, for convenience you can import these functions and fields like below, which will allow you do drop the 'self' prefix:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
class MyActor extends Actor {
|
||||
import self._
|
||||
id = ...
|
||||
dispatcher = ...
|
||||
spawnLink[OtherActor]
|
||||
...
|
||||
}
|
||||
|
||||
Serialization
|
||||
-------------
|
||||
|
||||
If you want to serialize it yourself, here is how to do it:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val actorRef1 = actorOf(Props[MyActor]
|
||||
|
||||
val bytes = actorRef1.toBinary
|
||||
|
||||
val actorRef2 = ActorRef.fromBinary(bytes)
|
||||
|
||||
If you are also using Protobuf then you can use the methods that work with Protobuf's Messages directly.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val actorRef1 = actorOf(Props[MyActor]
|
||||
|
||||
val protobufMessage = actorRef1.toProtocol
|
||||
|
||||
val actorRef2 = ActorRef.fromProtocol(protobufMessage)
|
||||
|
||||
Camel
|
||||
-----
|
||||
|
||||
Some methods of the se.scalablesolutions.akka.camel.Message class have been deprecated in 0.9. These are
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
package se.scalablesolutions.akka.camel
|
||||
|
||||
case class Message(...) {
|
||||
// ...
|
||||
@deprecated def bodyAs[T](clazz: Class[T]): T
|
||||
@deprecated def setBodyAs[T](clazz: Class[T]): Message
|
||||
// ...
|
||||
}
|
||||
|
||||
They will be removed in 1.0. Instead use
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
package se.scalablesolutions.akka.camel
|
||||
|
||||
case class Message(...) {
|
||||
// ...
|
||||
def bodyAs[T](implicit m: Manifest[T]): T =
|
||||
def setBodyAs[T](implicit m: Manifest[T]): Message
|
||||
// ...
|
||||
}
|
||||
|
||||
Usage example:
|
||||
.. code-block:: scala
|
||||
|
||||
val m = Message(1.4)
|
||||
val b = m.bodyAs[String]
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ along with the implementation of how the messages should be processed.
|
|||
|
||||
Here is an example:
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala
|
||||
:include: imports1,my-actor
|
||||
|
||||
Please note that the Akka Actor ``receive`` message loop is exhaustive, which is
|
||||
|
|
@ -53,7 +53,7 @@ thrown and the actor is restarted when an unknown message is received.
|
|||
Creating Actors with default constructor
|
||||
----------------------------------------
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala
|
||||
:include: imports2,system-actorOf
|
||||
|
||||
The call to :meth:`actorOf` returns an instance of ``ActorRef``. This is a handle to
|
||||
|
|
@ -70,7 +70,7 @@ how the supervisor hierarchy is arranged. When using the context the current act
|
|||
will be supervisor of the created child actor. When using the system it will be
|
||||
a top level actor, that is supervised by the system (internal guardian actor).
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#context-actorOf
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#context-actorOf
|
||||
|
||||
Actors are automatically started asynchronously when created.
|
||||
When you create the ``Actor`` then it will automatically call the ``preStart``
|
||||
|
|
@ -92,16 +92,24 @@ a call-by-name block in which you can create the Actor in any way you like.
|
|||
|
||||
Here is an example:
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#creating-constructor
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#creating-constructor
|
||||
|
||||
|
||||
Props
|
||||
-----
|
||||
|
||||
``Props`` is a configuration class to specify options for the creation
|
||||
of actors. Here are some examples on how to create a ``Props`` instance.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#creating-props-config
|
||||
|
||||
|
||||
Creating Actors with Props
|
||||
--------------------------
|
||||
|
||||
``Props`` is a configuration object to specify additional things for the actor to
|
||||
be created, such as the ``MessageDispatcher``.
|
||||
Actors are created by passing in a ``Props`` instance into the ``actorOf`` factory method.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#creating-props
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#creating-props
|
||||
|
||||
|
||||
Creating Actors using anonymous classes
|
||||
|
|
@ -109,7 +117,7 @@ Creating Actors using anonymous classes
|
|||
|
||||
When spawning actors for specific sub-tasks from within an actor, it may be convenient to include the code to be executed directly in place, using an anonymous class.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#anonymous-actor
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#anonymous-actor
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
@ -145,13 +153,16 @@ In addition, it offers:
|
|||
|
||||
You can import the members in the :obj:`context` to avoid prefixing access with ``context.``
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#import-context
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#import-context
|
||||
|
||||
The remaining visible methods are user-overridable life-cycle hooks which are
|
||||
described in the following::
|
||||
|
||||
def preStart() {}
|
||||
def preRestart(reason: Throwable, message: Option[Any]) { postStop() }
|
||||
def preRestart(reason: Throwable, message: Option[Any]) {
|
||||
context.children foreach (context.stop(_))
|
||||
postStop()
|
||||
}
|
||||
def postRestart(reason: Throwable) { preStart() }
|
||||
def postStop() {}
|
||||
|
||||
|
|
@ -185,7 +196,7 @@ processing a message. This restart involves the hooks mentioned above:
|
|||
message, e.g. when a supervisor does not trap the exception and is restarted
|
||||
in turn by its supervisor. This method is the best place for cleaning up,
|
||||
preparing hand-over to the fresh actor instance, etc.
|
||||
By default it calls :meth:`postStop`.
|
||||
By default it stops all children and calls :meth:`postStop`.
|
||||
2. The initial factory from the ``actorOf`` call is used
|
||||
to produce the fresh instance.
|
||||
3. The new actor’s :meth:`postRestart` method is invoked with the exception
|
||||
|
|
@ -287,7 +298,7 @@ To complete the future with an exception you need send a Failure message to the
|
|||
This is not done automatically when an actor throws an exception while processing a
|
||||
message.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#reply-exception
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#reply-exception
|
||||
|
||||
If the actor does not complete the future, it will expire after the timeout period,
|
||||
which is taken from one of the following locations in order of precedence:
|
||||
|
|
@ -336,7 +347,7 @@ type, it will throw the exception or a :class:`ClassCastException` (if you want
|
|||
to get :obj:`None` in the latter case, use :meth:`Future.asSilently[T]`). In
|
||||
case of a timeout, :obj:`None` is returned.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#using-ask
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#using-ask
|
||||
|
||||
Forward message
|
||||
---------------
|
||||
|
|
@ -368,7 +379,7 @@ This method should return a ``PartialFunction``, e.g. a ‘match/case’ clause
|
|||
which the message can be matched against the different case clauses using Scala
|
||||
pattern matching. Here is an example:
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala
|
||||
:include: imports1,my-actor
|
||||
|
||||
|
||||
|
|
@ -398,19 +409,17 @@ received within a certain time. To receive this timeout you have to set the
|
|||
``receiveTimeout`` property and declare a case handing the ReceiveTimeout
|
||||
object.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#receive-timeout
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#receive-timeout
|
||||
|
||||
|
||||
Stopping actors
|
||||
===============
|
||||
|
||||
Actors are stopped by invoking the ``stop`` method of the ``ActorRef``.
|
||||
The actual termination of the actor is performed asynchronously, i.e.
|
||||
``stop`` may return before the actor is stopped.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
actor.stop()
|
||||
Actors are stopped by invoking the :meth:`stop` method of a ``ActorRefFactory``,
|
||||
i.e. ``ActorContext`` or ``ActorSystem``. Typically the context is used for stopping
|
||||
child actors and the system for stopping top level actors. The actual termination of
|
||||
the actor is performed asynchronously, i.e. :meth:`stop` may return before the actor is
|
||||
stopped.
|
||||
|
||||
Processing of the current message, if any, will continue before the actor is stopped,
|
||||
but additional messages in the mailbox will not be processed. By default these
|
||||
|
|
@ -464,7 +473,7 @@ pushed and popped.
|
|||
|
||||
To hotswap the Actor behavior using ``become``:
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#hot-swap-actor
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#hot-swap-actor
|
||||
|
||||
The ``become`` method is useful for many different things, but a particular nice
|
||||
example of it is in example where it is used to implement a Finite State Machine
|
||||
|
|
@ -474,12 +483,12 @@ example of it is in example where it is used to implement a Finite State Machine
|
|||
|
||||
Here is another little cute example of ``become`` and ``unbecome`` in action:
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#swapper
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#swapper
|
||||
|
||||
Encoding Scala Actors nested receives without accidentally leaking memory
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
See this `Unnested receive example <http://github.com/jboner/akka/blob/master/akka/akka-docs/scala/code/UnnestedReceives.scala>`_.
|
||||
See this `Unnested receive example <http://github.com/jboner/akka/blob/master/akka/akka-docs/scala/code/akka/docs/actor/UnnestedReceives.scala>`_.
|
||||
|
||||
|
||||
Downgrade
|
||||
|
|
@ -555,4 +564,4 @@ A bit advanced but very useful way of defining a base message handler and then
|
|||
extend that, either through inheritance or delegation, is to use
|
||||
``PartialFunction.orElse`` chaining.
|
||||
|
||||
.. includecode:: code/ActorDocSpec.scala#receive-orElse
|
||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#receive-orElse
|
||||
|
|
|
|||
|
|
@ -1,147 +1,4 @@
|
|||
Agents (Scala)
|
||||
==============
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Agents in Akka were inspired by `agents in Clojure <http://clojure.org/agents>`_.
|
||||
|
||||
Agents provide asynchronous change of individual locations. Agents are bound to a single storage location for their lifetime, and only allow mutation of that location (to a new state) to occur as a result of an action. Update actions are functions that are asynchronously applied to the Agent's state and whose return value becomes the Agent's new state. The state of an Agent should be immutable.
|
||||
|
||||
While updates to Agents are asynchronous, the state of an Agent is always immediately available for reading by any thread (using ``get`` or ``apply``) without any messages.
|
||||
|
||||
Agents are reactive. The update actions of all Agents get interleaved amongst threads in a thread pool. At any point in time, at most one ``send`` action for each Agent is being executed. Actions dispatched to an agent from another thread will occur in the order they were sent, potentially interleaved with actions dispatched to the same agent from other sources.
|
||||
|
||||
If an Agent is used within an enclosing transaction, then it will participate in that transaction. 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.
|
||||
|
||||
Creating and stopping Agents
|
||||
----------------------------
|
||||
|
||||
Agents are created by invoking ``Agent(value)`` passing in the Agent's initial value.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val agent = Agent(5)
|
||||
|
||||
An Agent will be running until you invoke ``close`` on it. Then it will be eligible for garbage collection (unless you hold on to it in some way).
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
agent.close()
|
||||
|
||||
Updating Agents
|
||||
---------------
|
||||
|
||||
You update an Agent by sending a function that transforms the current value or by sending just a new value. The Agent will apply the new value or function atomically and asynchronously. The update is done in a fire-forget manner and you are only guaranteed that it will be applied. There is no guarantee of when the update will be applied but dispatches to an Agent from a single thread will occur in order. You apply a value or a function by invoking the ``send`` function.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
// send a value
|
||||
agent send 7
|
||||
|
||||
// send a function
|
||||
agent send (_ + 1)
|
||||
agent send (_ * 2)
|
||||
|
||||
You can also dispatch a function to update the internal state but on its own thread. This does not use the reactive thread pool and can be used for long-running or blocking operations. You do this with the ``sendOff`` method. Dispatches using either ``sendOff`` or ``send`` will still be executed in order.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
// sendOff a function
|
||||
agent sendOff (longRunningOrBlockingFunction)
|
||||
|
||||
Reading an Agent's value
|
||||
------------------------
|
||||
|
||||
Agents can be dereferenced, e.g. you can get an Agent's value, by invoking the Agent with parenthesis like this:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent()
|
||||
|
||||
Or by using the get method.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent.get
|
||||
|
||||
Reading an Agent's current value does not involve any message passing and happens immediately. So while updates to an Agent are asynchronous, reading the state of an Agent is synchronous.
|
||||
|
||||
Awaiting an Agent's value
|
||||
-------------------------
|
||||
|
||||
It is also possible to read the value after all currently queued ``send``\s have completed. You can do this with ``await``:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val result = agent.await
|
||||
|
||||
You can also get a ``Future`` to this value, that will be completed after the currently queued updates have completed:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val future = agent.future
|
||||
// ...
|
||||
val result = future.await.result.get
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
Agents are also monadic, allowing you to compose operations using for-comprehensions. In a monadic usage, new Agents are created leaving the original Agents untouched. So the old values (Agents) are still available as-is. They are so-called 'persistent'.
|
||||
|
||||
Example of a monadic usage:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val agent1 = Agent(3)
|
||||
val agent2 = Agent(5)
|
||||
|
||||
// uses foreach
|
||||
var result = 0
|
||||
for (value <- agent1) {
|
||||
result = value + 1
|
||||
}
|
||||
|
||||
// uses map
|
||||
val agent3 =
|
||||
for (value <- agent1) yield value + 1
|
||||
|
||||
// uses flatMap
|
||||
val agent4 = for {
|
||||
value1 <- agent1
|
||||
value2 <- agent2
|
||||
} yield value1 + value2
|
||||
|
||||
agent1.close()
|
||||
agent2.close()
|
||||
agent3.close()
|
||||
agent4.close()
|
||||
The Akka Agents module has not been migrated to Akka 2.0-SNAPSHOT yet.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package akka.docs.stm
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class StmDocSpec extends WordSpec with MustMatchers {
|
||||
|
||||
"simple counter example" in {
|
||||
//#simple
|
||||
import akka.stm._
|
||||
|
||||
val ref = Ref(0)
|
||||
|
||||
def counter = atomic {
|
||||
ref alter (_ + 1)
|
||||
}
|
||||
|
||||
counter
|
||||
// -> 1
|
||||
|
||||
counter
|
||||
// -> 2
|
||||
//#simple
|
||||
|
||||
ref.get must be === 2
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import akka.actor.ActorSystem
|
|||
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.testkit._
|
||||
import akka.util._
|
||||
import akka.util.duration._
|
||||
|
||||
//#my-actor
|
||||
|
|
@ -187,6 +188,23 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
|||
system.stop(myActor)
|
||||
}
|
||||
|
||||
"creating a Props config" in {
|
||||
val dispatcher = system.dispatcherFactory.lookup("my-dispatcher")
|
||||
//#creating-props-config
|
||||
import akka.actor.Props
|
||||
val props1 = Props()
|
||||
val props2 = Props[MyActor]
|
||||
val props3 = Props(new MyActor)
|
||||
val props4 = Props(
|
||||
creator = { () ⇒ new MyActor },
|
||||
dispatcher = dispatcher,
|
||||
timeout = Timeout(100))
|
||||
val props5 = props1.withCreator(new MyActor)
|
||||
val props6 = props5.withDispatcher(dispatcher)
|
||||
val props7 = props6.withTimeout(Timeout(100))
|
||||
//#creating-props-config
|
||||
}
|
||||
|
||||
"creating actor with Props" in {
|
||||
//#creating-props
|
||||
import akka.actor.Props
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue