Merge remote-tracking branch 'origin/master' into wip-remote-supervision-rk
This commit is contained in:
commit
92e7693203
247 changed files with 7896 additions and 4703 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -16,6 +16,7 @@ reports
|
|||
dist
|
||||
target
|
||||
deploy/*.jar
|
||||
.history
|
||||
data
|
||||
out
|
||||
logs
|
||||
|
|
@ -58,3 +59,4 @@ akka.sublime-project
|
|||
akka.sublime-workspace
|
||||
.target
|
||||
.multi-jvm
|
||||
_mb
|
||||
|
|
|
|||
4
.history
4
.history
|
|
@ -1,4 +0,0 @@
|
|||
update
|
||||
reload
|
||||
projects
|
||||
exit
|
||||
|
|
@ -26,7 +26,7 @@ public class JavaAPI {
|
|||
|
||||
@Test
|
||||
public void mustBeAbleToCreateActorRefFromClass() {
|
||||
ActorRef ref = system.actorOf(JavaAPITestActor.class);
|
||||
ActorRef ref = system.actorOf(new Props(JavaAPITestActor.class));
|
||||
assertNotNull(ref);
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ public class JavaAPI {
|
|||
|
||||
@Test
|
||||
public void mustAcceptSingleArgTell() {
|
||||
ActorRef ref = system.actorOf(JavaAPITestActor.class);
|
||||
ActorRef ref = system.actorOf(new Props(JavaAPITestActor.class));
|
||||
ref.tell("hallo");
|
||||
ref.tell("hallo", ref);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,16 +63,16 @@ class ActorFireForgetRequestReplySpec extends AkkaSpec with BeforeAndAfterEach w
|
|||
"An Actor" must {
|
||||
|
||||
"reply to bang message using reply" in {
|
||||
val replyActor = system.actorOf[ReplyActor]
|
||||
val senderActor = system.actorOf(new SenderActor(replyActor))
|
||||
val replyActor = system.actorOf(Props[ReplyActor])
|
||||
val senderActor = system.actorOf(Props(new SenderActor(replyActor)))
|
||||
senderActor ! "Init"
|
||||
state.finished.await
|
||||
state.s must be("Reply")
|
||||
}
|
||||
|
||||
"reply to bang message using implicit sender" in {
|
||||
val replyActor = system.actorOf[ReplyActor]
|
||||
val senderActor = system.actorOf(new SenderActor(replyActor))
|
||||
val replyActor = system.actorOf(Props[ReplyActor])
|
||||
val senderActor = system.actorOf(Props(new SenderActor(replyActor)))
|
||||
senderActor ! "InitImplicit"
|
||||
state.finished.await
|
||||
state.s must be("ReplyImplicit")
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout {
|
|||
a ! 42
|
||||
f.isCompleted must be === true
|
||||
f.get must be === 42
|
||||
system.actorFor(a.path) must be === system.deadLetters
|
||||
// clean-up is run as onComplete callback, i.e. dispatched on another thread
|
||||
awaitCond(system.actorFor(a.path) == system.deadLetters, 1 second)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -229,7 +230,8 @@ class ActorLookupSpec extends AkkaSpec with DefaultTimeout {
|
|||
a ! 42
|
||||
f.isCompleted must be === true
|
||||
f.get must be === 42
|
||||
(c2 ? LookupPath(a.path)).get must be === system.deadLetters
|
||||
// clean-up is run as onComplete callback, i.e. dispatched on another thread
|
||||
awaitCond((c2 ? LookupPath(a.path)).get == system.deadLetters, 1 second)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,82 +145,82 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
|
|||
filterException[akka.actor.ActorInitializationException] {
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new Actor {
|
||||
actorOf(Props(new Actor {
|
||||
val nested = promiseIntercept(new Actor { def receive = { case _ ⇒ } })(result)
|
||||
def receive = { case _ ⇒ }
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(promiseIntercept(new FailingOuterActor(actorOf(new InnerActor)))(result)))
|
||||
actorOf(Props(promiseIntercept(new FailingOuterActor(actorOf(Props(new InnerActor))))(result))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new OuterActor(actorOf(promiseIntercept(new FailingInnerActor)(result)))))
|
||||
actorOf(Props(new OuterActor(actorOf(Props(promiseIntercept(new FailingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(promiseIntercept(new FailingInheritingOuterActor(actorOf(new InnerActor)))(result)))
|
||||
actorOf(Props(promiseIntercept(new FailingInheritingOuterActor(actorOf(Props(new InnerActor))))(result))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new FailingOuterActor(actorOf(promiseIntercept(new FailingInheritingInnerActor)(result)))))
|
||||
actorOf(Props(new FailingOuterActor(actorOf(Props(promiseIntercept(new FailingInheritingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new FailingInheritingOuterActor(actorOf(promiseIntercept(new FailingInheritingInnerActor)(result)))))
|
||||
actorOf(Props(new FailingInheritingOuterActor(actorOf(Props(promiseIntercept(new FailingInheritingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new FailingInheritingOuterActor(actorOf(promiseIntercept(new FailingInnerActor)(result)))))
|
||||
actorOf(Props(new FailingInheritingOuterActor(actorOf(Props(promiseIntercept(new FailingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new OuterActor(actorOf(new InnerActor {
|
||||
actorOf(Props(new OuterActor(actorOf(Props(new InnerActor {
|
||||
val a = promiseIntercept(new InnerActor)(result)
|
||||
}))))
|
||||
}))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new FailingOuterActor(actorOf(promiseIntercept(new FailingInheritingInnerActor)(result)))))
|
||||
actorOf(Props(new FailingOuterActor(actorOf(Props(promiseIntercept(new FailingInheritingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new OuterActor(actorOf(promiseIntercept(new FailingInheritingInnerActor)(result)))))
|
||||
actorOf(Props(new OuterActor(actorOf(Props(promiseIntercept(new FailingInheritingInnerActor)(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new OuterActor(actorOf(promiseIntercept({ new InnerActor; new InnerActor })(result)))))
|
||||
actorOf(Props(new OuterActor(actorOf(Props(promiseIntercept({ new InnerActor; new InnerActor })(result)))))))
|
||||
}
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
|
@ -229,7 +229,7 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
|
|||
filterException[java.lang.IllegalStateException] {
|
||||
(intercept[java.lang.IllegalStateException] {
|
||||
wrap(result ⇒
|
||||
actorOf(new OuterActor(actorOf(promiseIntercept({ throw new IllegalStateException("Ur state be b0rked"); new InnerActor })(result)))))
|
||||
actorOf(Props(new OuterActor(actorOf(Props(promiseIntercept({ throw new IllegalStateException("Ur state be b0rked"); new InnerActor })(result)))))))
|
||||
}).getMessage must be === "Ur state be b0rked"
|
||||
|
||||
contextStackMustBeEmpty
|
||||
|
|
@ -237,7 +237,7 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
|
||||
"be serializable using Java Serialization on local node" in {
|
||||
val a = system.actorOf[InnerActor]
|
||||
val a = system.actorOf(Props[InnerActor])
|
||||
|
||||
import java.io._
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
|
||||
"throw an exception on deserialize if no system in scope" in {
|
||||
val a = system.actorOf[InnerActor]
|
||||
val a = system.actorOf(Props[InnerActor])
|
||||
|
||||
import java.io._
|
||||
|
||||
|
|
@ -301,10 +301,10 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
|
||||
"support nested actorOfs" in {
|
||||
val a = system.actorOf(new Actor {
|
||||
val nested = system.actorOf(new Actor { def receive = { case _ ⇒ } })
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
val nested = system.actorOf(Props(new Actor { def receive = { case _ ⇒ } }))
|
||||
def receive = { case _ ⇒ sender ! nested }
|
||||
})
|
||||
}))
|
||||
|
||||
val nested = (a ? "any").as[ActorRef].get
|
||||
a must not be null
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ object Chameneos {
|
|||
var numFaded = 0
|
||||
|
||||
override def preStart() = {
|
||||
for (i ← 0 until numChameneos) context.actorOf(new Chameneo(self, colours(i % 3), i))
|
||||
for (i ← 0 until numChameneos) context.actorOf(Props(new Chameneo(self, colours(i % 3), i)))
|
||||
}
|
||||
|
||||
def receive = {
|
||||
|
|
@ -107,7 +107,7 @@ object Chameneos {
|
|||
def run {
|
||||
// System.setProperty("akka.config", "akka.conf")
|
||||
Chameneos.start = System.currentTimeMillis
|
||||
val system = ActorSystem().actorOf(new Mall(1000000, 4))
|
||||
val system = ActorSystem().actorOf(Props(new Mall(1000000, 4)))
|
||||
Thread.sleep(10000)
|
||||
println("Elapsed: " + (end - start))
|
||||
system.stop()
|
||||
|
|
|
|||
|
|
@ -112,14 +112,14 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
|
|||
import latches._
|
||||
|
||||
// lock that locked after being open for 1 sec
|
||||
val lock = system.actorOf(new Lock("33221", 1 second, latches))
|
||||
val lock = system.actorOf(Props(new Lock("33221", 1 second, latches)))
|
||||
|
||||
val transitionTester = system.actorOf(new Actor {
|
||||
val transitionTester = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case Transition(_, _, _) ⇒ transitionCallBackLatch.open
|
||||
case CurrentState(_, Locked) ⇒ initialStateLatch.open
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
lock ! SubscribeTransitionCallBack(transitionTester)
|
||||
initialStateLatch.await
|
||||
|
|
@ -143,13 +143,13 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
|
|||
val answerLatch = TestLatch()
|
||||
object Hello
|
||||
object Bye
|
||||
val tester = system.actorOf(new Actor {
|
||||
val tester = system.actorOf(Props(new Actor {
|
||||
protected def receive = {
|
||||
case Hello ⇒ lock ! "hello"
|
||||
case "world" ⇒ answerLatch.open
|
||||
case Bye ⇒ lock ! "bye"
|
||||
}
|
||||
})
|
||||
}))
|
||||
tester ! Hello
|
||||
answerLatch.await
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
|
|||
case x ⇒ testActor ! x
|
||||
}
|
||||
}
|
||||
val ref = system.actorOf(fsm)
|
||||
val ref = system.actorOf(Props(fsm))
|
||||
started.await
|
||||
ref.stop()
|
||||
expectMsg(1 second, fsm.StopEvent(Shutdown, 1, null))
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class FSMTimingSpec extends AkkaSpec with ImplicitSender {
|
|||
import FSMTimingSpec._
|
||||
import FSM._
|
||||
|
||||
val fsm = system.actorOf(new StateMachine(testActor))
|
||||
val fsm = system.actorOf(Props(new StateMachine(testActor)))
|
||||
fsm ! SubscribeTransitionCallBack(testActor)
|
||||
expectMsg(1 second, CurrentState(fsm, Initial))
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class FSMTransitionSpec extends AkkaSpec with ImplicitSender {
|
|||
"A FSM transition notifier" must {
|
||||
|
||||
"notify listeners" in {
|
||||
val fsm = system.actorOf(new MyFSM(testActor))
|
||||
val fsm = system.actorOf(Props(new MyFSM(testActor)))
|
||||
within(1 second) {
|
||||
fsm ! SubscribeTransitionCallBack(testActor)
|
||||
expectMsg(CurrentState(fsm, 0))
|
||||
|
|
@ -68,8 +68,8 @@ class FSMTransitionSpec extends AkkaSpec with ImplicitSender {
|
|||
}
|
||||
|
||||
"not fail when listener goes away" in {
|
||||
val forward = system.actorOf(new Forwarder(testActor))
|
||||
val fsm = system.actorOf(new MyFSM(testActor))
|
||||
val forward = system.actorOf(Props(new Forwarder(testActor)))
|
||||
val fsm = system.actorOf(Props(new MyFSM(testActor)))
|
||||
val sup = system.actorOf(Props(new Actor {
|
||||
context.watch(fsm)
|
||||
def receive = { case _ ⇒ }
|
||||
|
|
@ -88,7 +88,7 @@ class FSMTransitionSpec extends AkkaSpec with ImplicitSender {
|
|||
"A FSM" must {
|
||||
|
||||
"make previous and next state data available in onTransition" in {
|
||||
val fsm = system.actorOf(new OtherFSM(testActor))
|
||||
val fsm = system.actorOf(Props(new OtherFSM(testActor)))
|
||||
within(300 millis) {
|
||||
fsm ! "tick"
|
||||
expectMsg((0, 1))
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ object ForwardActorSpec {
|
|||
val ExpectedMessage = "FOO"
|
||||
|
||||
def createForwardingChain(system: ActorSystem): ActorRef = {
|
||||
val replier = system.actorOf(new Actor {
|
||||
val replier = system.actorOf(Props(new Actor {
|
||||
def receive = { case x ⇒ sender ! x }
|
||||
})
|
||||
}))
|
||||
|
||||
def mkforwarder(forwardTo: ActorRef) = system.actorOf(
|
||||
def mkforwarder(forwardTo: ActorRef) = system.actorOf(Props(
|
||||
new Actor {
|
||||
def receive = { case x ⇒ forwardTo forward x }
|
||||
})
|
||||
}))
|
||||
|
||||
mkforwarder(mkforwarder(mkforwarder(replier)))
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ class ForwardActorSpec extends AkkaSpec {
|
|||
"forward actor reference when invoking forward on bang" in {
|
||||
val latch = new TestLatch(1)
|
||||
|
||||
val replyTo = system.actorOf(new Actor { def receive = { case ExpectedMessage ⇒ latch.countDown() } })
|
||||
val replyTo = system.actorOf(Props(new Actor { def receive = { case ExpectedMessage ⇒ latch.countDown() } }))
|
||||
|
||||
val chain = createForwardingChain(system)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,22 +11,13 @@ class HotSwapSpec extends AkkaSpec with ImplicitSender {
|
|||
|
||||
"An Actor" must {
|
||||
|
||||
"be able to hotswap its behavior with HotSwap(..)" in {
|
||||
val a = system.actorOf(new Actor {
|
||||
def receive = { case _ ⇒ sender ! "default" }
|
||||
})
|
||||
a ! HotSwap(context ⇒ { case _ ⇒ context.sender ! "swapped" })
|
||||
a ! "swapped"
|
||||
expectMsg("swapped")
|
||||
}
|
||||
|
||||
"be able to hotswap its behavior with become(..)" in {
|
||||
val a = system.actorOf(new Actor {
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "init" ⇒ sender ! "init"
|
||||
case "swap" ⇒ context.become({ case x: String ⇒ context.sender ! x })
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
a ! "init"
|
||||
expectMsg("init")
|
||||
|
|
@ -35,34 +26,8 @@ class HotSwapSpec extends AkkaSpec with ImplicitSender {
|
|||
expectMsg("swapped")
|
||||
}
|
||||
|
||||
"be able to revert hotswap its behavior with RevertHotSwap(..)" in {
|
||||
val a = system.actorOf(new Actor {
|
||||
def receive = {
|
||||
case "init" ⇒ sender ! "init"
|
||||
}
|
||||
})
|
||||
|
||||
a ! "init"
|
||||
expectMsg("init")
|
||||
a ! HotSwap(context ⇒ { case "swapped" ⇒ context.sender ! "swapped" })
|
||||
|
||||
a ! "swapped"
|
||||
expectMsg("swapped")
|
||||
|
||||
a ! RevertHotSwap
|
||||
|
||||
a ! "init"
|
||||
expectMsg("init")
|
||||
|
||||
// try to revert hotswap below the bottom of the stack
|
||||
a ! RevertHotSwap
|
||||
|
||||
a ! "init"
|
||||
expectMsg("init")
|
||||
}
|
||||
|
||||
"be able to revert hotswap its behavior with unbecome" in {
|
||||
val a = system.actorOf(new Actor {
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "init" ⇒ sender ! "init"
|
||||
case "swap" ⇒
|
||||
|
|
@ -73,7 +38,7 @@ class HotSwapSpec extends AkkaSpec with ImplicitSender {
|
|||
context.unbecome()
|
||||
})
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
a ! "init"
|
||||
expectMsg("init")
|
||||
|
|
@ -89,7 +54,7 @@ class HotSwapSpec extends AkkaSpec with ImplicitSender {
|
|||
|
||||
"revert to initial state on restart" in {
|
||||
|
||||
val a = system.actorOf(new Actor {
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "state" ⇒ sender ! "0"
|
||||
case "swap" ⇒
|
||||
|
|
@ -100,7 +65,7 @@ class HotSwapSpec extends AkkaSpec with ImplicitSender {
|
|||
})
|
||||
sender ! "swapped"
|
||||
}
|
||||
})
|
||||
}))
|
||||
a ! "state"
|
||||
expectMsg("0")
|
||||
a ! "swap"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ object IOActorSpec {
|
|||
class SimpleEchoClient(host: String, port: Int, ioManager: ActorRef) extends Actor with IO {
|
||||
|
||||
lazy val socket: SocketHandle = connect(ioManager, host, port)(reader)
|
||||
lazy val reader: ActorRef = context.actorOf {
|
||||
lazy val reader: ActorRef = context.actorOf(Props({
|
||||
new Actor with IO {
|
||||
def receiveIO = {
|
||||
case length: Int ⇒
|
||||
|
|
@ -54,7 +54,7 @@ object IOActorSpec {
|
|||
sender ! bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
def receiveIO = {
|
||||
case bytes: ByteString ⇒
|
||||
|
|
@ -186,10 +186,10 @@ class IOActorSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout {
|
|||
"an IO Actor" must {
|
||||
"run echo server" in {
|
||||
val started = TestLatch(1)
|
||||
val ioManager = system.actorOf(new IOManager(2)) // teeny tiny buffer
|
||||
val server = system.actorOf(new SimpleEchoServer("localhost", 8064, ioManager, started))
|
||||
val ioManager = system.actorOf(Props(new IOManager(2))) // teeny tiny buffer
|
||||
val server = system.actorOf(Props(new SimpleEchoServer("localhost", 8064, ioManager, started)))
|
||||
started.await
|
||||
val client = system.actorOf(new SimpleEchoClient("localhost", 8064, ioManager))
|
||||
val client = system.actorOf(Props(new SimpleEchoClient("localhost", 8064, ioManager)))
|
||||
val f1 = client ? ByteString("Hello World!1")
|
||||
val f2 = client ? ByteString("Hello World!2")
|
||||
val f3 = client ? ByteString("Hello World!3")
|
||||
|
|
@ -203,10 +203,10 @@ class IOActorSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout {
|
|||
|
||||
"run echo server under high load" in {
|
||||
val started = TestLatch(1)
|
||||
val ioManager = system.actorOf(new IOManager())
|
||||
val server = system.actorOf(new SimpleEchoServer("localhost", 8065, ioManager, started))
|
||||
val ioManager = system.actorOf(Props(new IOManager()))
|
||||
val server = system.actorOf(Props(new SimpleEchoServer("localhost", 8065, ioManager, started)))
|
||||
started.await
|
||||
val client = system.actorOf(new SimpleEchoClient("localhost", 8065, ioManager))
|
||||
val client = system.actorOf(Props(new SimpleEchoClient("localhost", 8065, ioManager)))
|
||||
val list = List.range(0, 1000)
|
||||
val f = Future.traverse(list)(i ⇒ client ? ByteString(i.toString))
|
||||
assert(f.get.size === 1000)
|
||||
|
|
@ -217,10 +217,10 @@ class IOActorSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout {
|
|||
|
||||
"run echo server under high load with small buffer" in {
|
||||
val started = TestLatch(1)
|
||||
val ioManager = system.actorOf(new IOManager(2))
|
||||
val server = system.actorOf(new SimpleEchoServer("localhost", 8066, ioManager, started))
|
||||
val ioManager = system.actorOf(Props(new IOManager(2)))
|
||||
val server = system.actorOf(Props(new SimpleEchoServer("localhost", 8066, ioManager, started)))
|
||||
started.await
|
||||
val client = system.actorOf(new SimpleEchoClient("localhost", 8066, ioManager))
|
||||
val client = system.actorOf(Props(new SimpleEchoClient("localhost", 8066, ioManager)))
|
||||
val list = List.range(0, 1000)
|
||||
val f = Future.traverse(list)(i ⇒ client ? ByteString(i.toString))
|
||||
assert(f.get.size === 1000)
|
||||
|
|
@ -231,11 +231,11 @@ class IOActorSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout {
|
|||
|
||||
"run key-value store" in {
|
||||
val started = TestLatch(1)
|
||||
val ioManager = system.actorOf(new IOManager(2)) // teeny tiny buffer
|
||||
val server = system.actorOf(new KVStore("localhost", 8067, ioManager, started))
|
||||
val ioManager = system.actorOf(Props(new IOManager(2))) // teeny tiny buffer
|
||||
val server = system.actorOf(Props(new KVStore("localhost", 8067, ioManager, started)))
|
||||
started.await
|
||||
val client1 = system.actorOf(new KVClient("localhost", 8067, ioManager))
|
||||
val client2 = system.actorOf(new KVClient("localhost", 8067, ioManager))
|
||||
val client1 = system.actorOf(Props(new KVClient("localhost", 8067, ioManager)))
|
||||
val client2 = system.actorOf(Props(new KVClient("localhost", 8067, ioManager)))
|
||||
val f1 = client1 ? (('set, "hello", ByteString("World")))
|
||||
val f2 = client1 ? (('set, "test", ByteString("No one will read me")))
|
||||
val f3 = client1 ? (('get, "hello"))
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ class LocalActorRefProviderSpec extends AkkaSpec {
|
|||
}
|
||||
|
||||
"only create one instance of an actor from within the same message invocation" in {
|
||||
val supervisor = system.actorOf(new Actor {
|
||||
val supervisor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "" ⇒
|
||||
val a, b = context.actorOf(Props.empty, "duplicate")
|
||||
}
|
||||
})
|
||||
}))
|
||||
EventFilter[InvalidActorNameException](occurrences = 1) intercept {
|
||||
supervisor ! ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,53 +17,30 @@ class ReceiveTimeoutSpec extends AkkaSpec {
|
|||
"get timeout" in {
|
||||
val timeoutLatch = TestLatch()
|
||||
|
||||
val timeoutActor = system.actorOf(new Actor {
|
||||
context.receiveTimeout = Some(500 milliseconds)
|
||||
val timeoutActor = system.actorOf(Props(new Actor {
|
||||
context.setReceiveTimeout(500 milliseconds)
|
||||
|
||||
protected def receive = {
|
||||
case ReceiveTimeout ⇒ timeoutLatch.open
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
timeoutLatch.await
|
||||
timeoutActor.stop()
|
||||
}
|
||||
|
||||
"get timeout when swapped" in {
|
||||
val timeoutLatch = TestLatch()
|
||||
|
||||
val timeoutActor = system.actorOf(new Actor {
|
||||
context.receiveTimeout = Some(500 milliseconds)
|
||||
|
||||
protected def receive = {
|
||||
case ReceiveTimeout ⇒ timeoutLatch.open
|
||||
}
|
||||
})
|
||||
|
||||
timeoutLatch.await
|
||||
|
||||
val swappedLatch = TestLatch()
|
||||
|
||||
timeoutActor ! HotSwap(context ⇒ {
|
||||
case ReceiveTimeout ⇒ swappedLatch.open
|
||||
})
|
||||
|
||||
swappedLatch.await
|
||||
timeoutActor.stop()
|
||||
}
|
||||
|
||||
"reschedule timeout after regular receive" in {
|
||||
val timeoutLatch = TestLatch()
|
||||
case object Tick
|
||||
|
||||
val timeoutActor = system.actorOf(new Actor {
|
||||
context.receiveTimeout = Some(500 milliseconds)
|
||||
val timeoutActor = system.actorOf(Props(new Actor {
|
||||
context.setReceiveTimeout(500 milliseconds)
|
||||
|
||||
protected def receive = {
|
||||
case Tick ⇒ ()
|
||||
case ReceiveTimeout ⇒ timeoutLatch.open
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
timeoutActor ! Tick
|
||||
|
||||
|
|
@ -76,17 +53,17 @@ class ReceiveTimeoutSpec extends AkkaSpec {
|
|||
val timeoutLatch = TestLatch()
|
||||
case object Tick
|
||||
|
||||
val timeoutActor = system.actorOf(new Actor {
|
||||
context.receiveTimeout = Some(500 milliseconds)
|
||||
val timeoutActor = system.actorOf(Props(new Actor {
|
||||
context.setReceiveTimeout(500 milliseconds)
|
||||
|
||||
protected def receive = {
|
||||
case Tick ⇒ ()
|
||||
case ReceiveTimeout ⇒
|
||||
count.incrementAndGet
|
||||
timeoutLatch.open
|
||||
context.receiveTimeout = None
|
||||
context.resetReceiveTimeout()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
timeoutActor ! Tick
|
||||
|
||||
|
|
@ -98,11 +75,11 @@ class ReceiveTimeoutSpec extends AkkaSpec {
|
|||
"not receive timeout message when not specified" in {
|
||||
val timeoutLatch = TestLatch()
|
||||
|
||||
val timeoutActor = system.actorOf(new Actor {
|
||||
val timeoutActor = system.actorOf(Props(new Actor {
|
||||
protected def receive = {
|
||||
case ReceiveTimeout ⇒ timeoutLatch.open
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
timeoutLatch.awaitTimeout(1 second) // timeout expected
|
||||
timeoutActor.stop()
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
"schedule more than once" in {
|
||||
case object Tick
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val tickActor = system.actorOf(new Actor {
|
||||
val tickActor = system.actorOf(Props(new Actor {
|
||||
def receive = { case Tick ⇒ countDownLatch.countDown() }
|
||||
})
|
||||
}))
|
||||
// run every 50 milliseconds
|
||||
collectCancellable(system.scheduler.schedule(0 milliseconds, 50 milliseconds, tickActor, Tick))
|
||||
|
||||
|
|
@ -56,9 +56,9 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
"schedule once" in {
|
||||
case object Tick
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val tickActor = system.actorOf(new Actor {
|
||||
val tickActor = system.actorOf(Props(new Actor {
|
||||
def receive = { case Tick ⇒ countDownLatch.countDown() }
|
||||
})
|
||||
}))
|
||||
|
||||
// run after 300 millisec
|
||||
collectCancellable(system.scheduler.scheduleOnce(300 milliseconds, tickActor, Tick))
|
||||
|
|
@ -81,9 +81,9 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
object Ping
|
||||
val ticks = new CountDownLatch(1)
|
||||
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = { case Ping ⇒ ticks.countDown() }
|
||||
})
|
||||
}))
|
||||
|
||||
(1 to 10).foreach { i ⇒
|
||||
val timeout = collectCancellable(system.scheduler.scheduleOnce(1 second, actor, Ping))
|
||||
|
|
@ -131,7 +131,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
|
||||
case class Msg(ts: Long)
|
||||
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case Msg(ts) ⇒
|
||||
val now = System.nanoTime
|
||||
|
|
@ -139,7 +139,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
if (now - ts < 10000000) throw new RuntimeException("Interval is too small: " + (now - ts))
|
||||
ticks.countDown()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
(1 to 300).foreach { i ⇒
|
||||
collectCancellable(system.scheduler.scheduleOnce(10 milliseconds, actor, Msg(System.nanoTime)))
|
||||
|
|
@ -154,11 +154,11 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
|
||||
case object Msg
|
||||
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case Msg ⇒ ticks.countDown()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val startTime = System.nanoTime()
|
||||
val cancellable = system.scheduler.schedule(1 second, 100 milliseconds, actor, Msg)
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ class TypedActorSpec extends AkkaSpec with BeforeAndAfterEach with BeforeAndAfte
|
|||
mustStop(t)
|
||||
}
|
||||
|
||||
"be able to use work-stealing dispatcher" in {
|
||||
"be able to use balancing dispatcher" in {
|
||||
val props = Props(
|
||||
timeout = Timeout(6600),
|
||||
dispatcher = system.dispatcherFactory.newBalancingDispatcher("pooled-dispatcher")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import akka.actor.Actor
|
||||
import akka.actor._
|
||||
import akka.testkit.AkkaSpec
|
||||
|
||||
/**
|
||||
* Tests the behavior of the executor based event driven dispatcher when multiple actors are being dispatched on it.
|
||||
*
|
||||
* @author Jan Van Besien
|
||||
*/
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class DispatcherActorsSpec extends AkkaSpec {
|
||||
|
|
@ -33,8 +31,8 @@ class DispatcherActorsSpec extends AkkaSpec {
|
|||
"not block fast actors by slow actors" in {
|
||||
val sFinished = new CountDownLatch(50)
|
||||
val fFinished = new CountDownLatch(10)
|
||||
val s = system.actorOf(new SlowActor(sFinished))
|
||||
val f = system.actorOf(new FastActor(fFinished))
|
||||
val s = system.actorOf(Props(new SlowActor(sFinished)))
|
||||
val f = system.actorOf(Props(new FastActor(fFinished)))
|
||||
|
||||
// send a lot of stuff to s
|
||||
for (i ← 1 to 50) {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,18 @@ import akka.testkit.AkkaSpec
|
|||
import scala.collection.JavaConverters._
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
object DispatchersSpec {
|
||||
val config = """
|
||||
myapp {
|
||||
mydispatcher {
|
||||
throughput = 17
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class DispatchersSpec extends AkkaSpec {
|
||||
class DispatchersSpec extends AkkaSpec(DispatchersSpec.config) {
|
||||
|
||||
val df = system.dispatcherFactory
|
||||
import df._
|
||||
|
|
@ -34,14 +44,6 @@ class DispatchersSpec extends AkkaSpec {
|
|||
|
||||
val defaultDispatcherConfig = settings.config.getConfig("akka.actor.default-dispatcher")
|
||||
|
||||
val dispatcherConf = ConfigFactory.parseString("""
|
||||
myapp {
|
||||
mydispatcher {
|
||||
throughput = 17
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
lazy val allDispatchers: Map[String, Option[MessageDispatcher]] = {
|
||||
validTypes.map(t ⇒ (t, from(ConfigFactory.parseMap(Map(tipe -> t).asJava).withFallback(defaultDispatcherConfig)))).toMap
|
||||
}
|
||||
|
|
@ -58,16 +60,21 @@ class DispatchersSpec extends AkkaSpec {
|
|||
dispatcher.map(_.throughput) must be(Some(17))
|
||||
}
|
||||
|
||||
"use defined properties when fromConfig" in {
|
||||
val dispatcher = fromConfig("myapp.mydispatcher", cfg = dispatcherConf)
|
||||
"use defined properties when newFromConfig" in {
|
||||
val dispatcher = newFromConfig("myapp.mydispatcher")
|
||||
dispatcher.throughput must be(17)
|
||||
}
|
||||
|
||||
"use specific name when fromConfig" in {
|
||||
val dispatcher = fromConfig("myapp.mydispatcher", cfg = dispatcherConf)
|
||||
"use specific name when newFromConfig" in {
|
||||
val dispatcher = newFromConfig("myapp.mydispatcher")
|
||||
dispatcher.name must be("mydispatcher")
|
||||
}
|
||||
|
||||
"use default dispatcher when not configured" in {
|
||||
val dispatcher = newFromConfig("myapp.other-dispatcher")
|
||||
dispatcher must be === defaultGlobalDispatcher
|
||||
}
|
||||
|
||||
"throw IllegalArgumentException if type does not exist" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
from(ConfigFactory.parseMap(Map(tipe -> "typedoesntexist").asJava).withFallback(defaultDispatcherConfig))
|
||||
|
|
@ -81,6 +88,13 @@ class DispatchersSpec extends AkkaSpec {
|
|||
assert(typesAndValidators.forall(tuple ⇒ tuple._2(allDispatchers(tuple._1).get)))
|
||||
}
|
||||
|
||||
"provide lookup of dispatchers by key" in {
|
||||
val d1 = lookup("myapp.mydispatcher")
|
||||
val d2 = lookup("myapp.mydispatcher")
|
||||
d1 must be === d2
|
||||
d1.name must be("mydispatcher")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ class ListenerSpec extends AkkaSpec {
|
|||
val barLatch = TestLatch(2)
|
||||
val barCount = new AtomicInteger(0)
|
||||
|
||||
val broadcast = system.actorOf(new Actor with Listeners {
|
||||
val broadcast = system.actorOf(Props(new Actor with Listeners {
|
||||
def receive = listenerManagement orElse {
|
||||
case "foo" ⇒ gossip("bar")
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
def newListener = system.actorOf(new Actor {
|
||||
def newListener = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "bar" ⇒
|
||||
barCount.incrementAndGet
|
||||
|
|
@ -30,7 +30,7 @@ class ListenerSpec extends AkkaSpec {
|
|||
case "foo" ⇒
|
||||
fooLatch.countDown()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val a1 = newListener
|
||||
val a2 = newListener
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import org.scalacheck._
|
|||
import org.scalacheck.Arbitrary._
|
||||
import org.scalacheck.Prop._
|
||||
import org.scalacheck.Gen._
|
||||
import akka.actor.{ Actor, ActorRef, Status }
|
||||
import akka.actor._
|
||||
import akka.testkit.{ EventFilter, filterEvents, filterException }
|
||||
import akka.util.duration._
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
|
|
@ -116,7 +116,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"from an Actor" that {
|
||||
"returns a result" must {
|
||||
behave like futureWithResult { test ⇒
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
val future = actor ? "Hello"
|
||||
future.await
|
||||
test(future, "World")
|
||||
|
|
@ -126,7 +126,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"throws an exception" must {
|
||||
behave like futureWithException[RuntimeException] { test ⇒
|
||||
filterException[RuntimeException] {
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
val future = actor ? "Failure"
|
||||
future.await
|
||||
test(future, "Expected exception; to test fault-tolerance")
|
||||
|
|
@ -139,8 +139,8 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"using flatMap with an Actor" that {
|
||||
"will return a result" must {
|
||||
behave like futureWithResult { test ⇒
|
||||
val actor1 = system.actorOf[TestActor]
|
||||
val actor2 = system.actorOf(new Actor { def receive = { case s: String ⇒ sender ! s.toUpperCase } })
|
||||
val actor1 = system.actorOf(Props[TestActor])
|
||||
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String ⇒ sender ! s.toUpperCase } }))
|
||||
val future = actor1 ? "Hello" flatMap { case s: String ⇒ actor2 ? s }
|
||||
future.await
|
||||
test(future, "WORLD")
|
||||
|
|
@ -151,8 +151,8 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"will throw an exception" must {
|
||||
behave like futureWithException[ArithmeticException] { test ⇒
|
||||
filterException[ArithmeticException] {
|
||||
val actor1 = system.actorOf[TestActor]
|
||||
val actor2 = system.actorOf(new Actor { def receive = { case s: String ⇒ sender ! Status.Failure(new ArithmeticException("/ by zero")) } })
|
||||
val actor1 = system.actorOf(Props[TestActor])
|
||||
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String ⇒ sender ! Status.Failure(new ArithmeticException("/ by zero")) } }))
|
||||
val future = actor1 ? "Hello" flatMap { case s: String ⇒ actor2 ? s }
|
||||
future.await
|
||||
test(future, "/ by zero")
|
||||
|
|
@ -164,8 +164,8 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"will throw a MatchError when matching wrong type" must {
|
||||
behave like futureWithException[MatchError] { test ⇒
|
||||
filterException[MatchError] {
|
||||
val actor1 = system.actorOf[TestActor]
|
||||
val actor2 = system.actorOf(new Actor { def receive = { case s: String ⇒ sender ! s.toUpperCase } })
|
||||
val actor1 = system.actorOf(Props[TestActor])
|
||||
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String ⇒ sender ! s.toUpperCase } }))
|
||||
val future = actor1 ? "Hello" flatMap { case i: Int ⇒ actor2 ? i }
|
||||
future.await
|
||||
test(future, "World (of class java.lang.String)")
|
||||
|
|
@ -180,12 +180,12 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
"compose with for-comprehensions" in {
|
||||
filterException[ClassCastException] {
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case s: String ⇒ sender ! s.length
|
||||
case i: Int ⇒ sender ! (i * 2).toString
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val future0 = actor ? "Hello"
|
||||
|
||||
|
|
@ -212,12 +212,12 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
filterException[MatchError] {
|
||||
case class Req[T](req: T)
|
||||
case class Res[T](res: T)
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case Req(s: String) ⇒ sender ! Res(s.length)
|
||||
case Req(i: Int) ⇒ sender ! Res((i * 2).toString)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val future1 = for {
|
||||
Res(a: Int) ← actor ? Req("Hello")
|
||||
|
|
@ -257,7 +257,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
val future7 = future3 recover { case e: ArithmeticException ⇒ "You got ERROR" }
|
||||
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val future8 = actor ? "Failure"
|
||||
val future9 = actor ? "Failure" recover {
|
||||
|
|
@ -300,9 +300,9 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
"fold" in {
|
||||
val actors = (1 to 10).toList map { _ ⇒
|
||||
system.actorOf(new Actor {
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = { case (add: Int, wait: Int) ⇒ Thread.sleep(wait); sender.tell(add) }
|
||||
})
|
||||
}))
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] }
|
||||
|
|
@ -311,9 +311,9 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
"fold by composing" in {
|
||||
val actors = (1 to 10).toList map { _ ⇒
|
||||
system.actorOf(new Actor {
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = { case (add: Int, wait: Int) ⇒ Thread.sleep(wait); sender.tell(add) }
|
||||
})
|
||||
}))
|
||||
}
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), 10000).mapTo[Int] }
|
||||
futures.foldLeft(Future(0))((fr, fa) ⇒ for (r ← fr; a ← fa) yield (r + a)).get must be(45)
|
||||
|
|
@ -322,14 +322,14 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"fold with an exception" in {
|
||||
filterException[IllegalArgumentException] {
|
||||
val actors = (1 to 10).toList map { _ ⇒
|
||||
system.actorOf(new Actor {
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case (add: Int, wait: Int) ⇒
|
||||
Thread.sleep(wait)
|
||||
if (add == 6) sender ! Status.Failure(new IllegalArgumentException("shouldFoldResultsWithException: expected"))
|
||||
else sender.tell(add)
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] }
|
||||
|
|
@ -358,9 +358,9 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
"shouldReduceResults" in {
|
||||
val actors = (1 to 10).toList map { _ ⇒
|
||||
system.actorOf(new Actor {
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = { case (add: Int, wait: Int) ⇒ Thread.sleep(wait); sender.tell(add) }
|
||||
})
|
||||
}))
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] }
|
||||
|
|
@ -370,14 +370,14 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"shouldReduceResultsWithException" in {
|
||||
filterException[IllegalArgumentException] {
|
||||
val actors = (1 to 10).toList map { _ ⇒
|
||||
system.actorOf(new Actor {
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case (add: Int, wait: Int) ⇒
|
||||
Thread.sleep(wait)
|
||||
if (add == 6) sender ! Status.Failure(new IllegalArgumentException("shouldFoldResultsWithException: expected"))
|
||||
else sender.tell(add)
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] }
|
||||
|
|
@ -393,21 +393,21 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
|
||||
"receiveShouldExecuteOnComplete" in {
|
||||
val latch = new StandardLatch
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
actor ? "Hello" onResult { case "World" ⇒ latch.open }
|
||||
assert(latch.tryAwait(5, TimeUnit.SECONDS))
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
"shouldTraverseFutures" in {
|
||||
val oddActor = system.actorOf(new Actor {
|
||||
val oddActor = system.actorOf(Props(new Actor {
|
||||
var counter = 1
|
||||
def receive = {
|
||||
case 'GetNext ⇒
|
||||
sender ! counter
|
||||
counter += 2
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val oddFutures = List.fill(100)(oddActor ? 'GetNext mapTo manifest[Int])
|
||||
assert(Future.sequence(oddFutures).get.sum === 10000)
|
||||
|
|
@ -461,7 +461,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
"futureComposingWithContinuations" in {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x flatMap (actor ? _) mapTo manifest[String]
|
||||
|
|
@ -490,7 +490,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
filterException[ClassCastException] {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future(3)
|
||||
val y = (actor ? "Hello").mapTo[Int]
|
||||
|
|
@ -505,7 +505,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
filterException[ClassCastException] {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf[TestActor]
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = actor ? "Hello" mapTo manifest[Nothing]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import scala.collection.JavaConverters._
|
|||
import java.util.Properties
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.HotSwap
|
||||
import akka.actor.UnhandledMessageException
|
||||
import akka.actor.PoisonPill
|
||||
import akka.actor.ActorSystemImpl
|
||||
|
|
@ -77,22 +76,27 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
|
|||
ignoreMute(this)
|
||||
system.eventStream.subscribe(testActor, classOf[Logging.Debug])
|
||||
system.eventStream.subscribe(testActor, classOf[Logging.Error])
|
||||
|
||||
val r: Actor.Receive = {
|
||||
case null ⇒
|
||||
}
|
||||
|
||||
val actor = TestActorRef(new Actor {
|
||||
def receive = LoggingReceive(this) {
|
||||
case x ⇒
|
||||
sender ! "x"
|
||||
def switch: Actor.Receive = { case "becomenull" ⇒ context.become(r, false) }
|
||||
def receive = switch orElse LoggingReceive(this) {
|
||||
case x ⇒ sender ! "x"
|
||||
}
|
||||
})
|
||||
|
||||
val name = actor.path.toString
|
||||
actor ! "buh"
|
||||
within(1 second) {
|
||||
expectMsg(Logging.Debug(name, "received handled message buh"))
|
||||
expectMsg("x")
|
||||
}
|
||||
val r: Actor.Receive = {
|
||||
case null ⇒
|
||||
}
|
||||
actor ! HotSwap(_ ⇒ r, false)
|
||||
|
||||
actor ! "becomenull"
|
||||
|
||||
EventFilter[UnhandledMessageException](pattern = "does not handle", occurrences = 1) intercept {
|
||||
within(500 millis) {
|
||||
actor ! "bah"
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ class TellLatencyPerformanceSpec extends PerformanceSpec {
|
|||
val latch = new CountDownLatch(numberOfClients)
|
||||
val repeatsPerClient = repeat / numberOfClients
|
||||
val clients = (for (i ← 0 until numberOfClients) yield {
|
||||
val destination = system.actorOf[Destination]
|
||||
val w4 = system.actorOf(new Waypoint(destination))
|
||||
val w3 = system.actorOf(new Waypoint(w4))
|
||||
val w2 = system.actorOf(new Waypoint(w3))
|
||||
val w1 = system.actorOf(new Waypoint(w2))
|
||||
val destination = system.actorOf(Props[Destination])
|
||||
val w4 = system.actorOf(Props(new Waypoint(destination)))
|
||||
val w3 = system.actorOf(Props(new Waypoint(w4)))
|
||||
val w2 = system.actorOf(Props(new Waypoint(w3)))
|
||||
val w1 = system.actorOf(Props(new Waypoint(w2)))
|
||||
Props(new Client(w1, latch, repeatsPerClient, clientDelay.toMicros.intValue, stat)).withDispatcher(clientDispatcher)
|
||||
}).toList.map(system.actorOf(_))
|
||||
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ class ActorPoolSpec extends AkkaSpec with DefaultTimeout {
|
|||
}).withFaultHandler(faultHandler))
|
||||
|
||||
val successes = TestLatch(2)
|
||||
val successCounter = system.actorOf(new Actor {
|
||||
val successCounter = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "success" ⇒ successes.countDown()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
implicit val replyTo = successCounter
|
||||
pool ! "a"
|
||||
|
|
|
|||
|
|
@ -109,12 +109,12 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
for (i ← 0 until connectionCount) {
|
||||
counters = counters :+ new AtomicInteger()
|
||||
|
||||
val actor = system.actorOf(new Actor {
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counters.get(i).get.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
actors = actors :+ actor
|
||||
}
|
||||
|
||||
|
|
@ -141,20 +141,20 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val doneLatch = new CountDownLatch(2)
|
||||
|
||||
val counter1 = new AtomicInteger
|
||||
val actor1 = system.actorOf(new Actor {
|
||||
val actor1 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter1.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val counter2 = new AtomicInteger
|
||||
val actor2 = system.actorOf(new Actor {
|
||||
val actor2 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter2.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RoundRobinRouter(targets = List(actor1, actor2))))
|
||||
|
||||
|
|
@ -179,20 +179,20 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val doneLatch = new CountDownLatch(2)
|
||||
|
||||
val counter1 = new AtomicInteger
|
||||
val actor1 = system.actorOf(new Actor {
|
||||
val actor1 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter1.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val counter2 = new AtomicInteger
|
||||
val actor2 = system.actorOf(new Actor {
|
||||
val actor2 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter2.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(RandomRouter(targets = List(actor1, actor2))))
|
||||
|
||||
|
|
@ -216,20 +216,20 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val doneLatch = new CountDownLatch(2)
|
||||
|
||||
val counter1 = new AtomicInteger
|
||||
val actor1 = system.actorOf(new Actor {
|
||||
val actor1 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter1.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val counter2 = new AtomicInteger
|
||||
val actor2 = system.actorOf(new Actor {
|
||||
val actor2 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter2.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
routedActor ! 1
|
||||
|
|
@ -245,22 +245,22 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val doneLatch = new CountDownLatch(2)
|
||||
|
||||
val counter1 = new AtomicInteger
|
||||
val actor1 = system.actorOf(new Actor {
|
||||
val actor1 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒
|
||||
counter1.addAndGet(msg)
|
||||
sender ! "ack"
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val counter2 = new AtomicInteger
|
||||
val actor2 = system.actorOf(new Actor {
|
||||
val actor2 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter2.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(BroadcastRouter(targets = List(actor1, actor2))))
|
||||
routedActor ? 1
|
||||
|
|
@ -284,20 +284,20 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
|
|||
val doneLatch = new TestLatch(2)
|
||||
|
||||
val counter1 = new AtomicInteger
|
||||
val actor1 = system.actorOf(new Actor {
|
||||
val actor1 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter1.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val counter2 = new AtomicInteger
|
||||
val actor2 = system.actorOf(new Actor {
|
||||
val actor2 = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counter2.addAndGet(msg)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
val routedActor = system.actorOf(Props(new TestActor).withRouter(ScatterGatherFirstCompletedRouter(targets = List(actor1, actor2))))
|
||||
routedActor ! Broadcast(1)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@ package akka.serialization
|
|||
import akka.serialization.Serialization._
|
||||
import scala.reflect._
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.actor.{ ActorSystem, ActorSystemImpl }
|
||||
import java.io.{ ObjectInputStream, ByteArrayInputStream, ByteArrayOutputStream, ObjectOutputStream }
|
||||
import akka.actor.DeadLetterActorRef
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import akka.actor._
|
||||
import java.io._
|
||||
|
||||
object SerializeSpec {
|
||||
|
||||
|
|
@ -94,6 +93,22 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
|||
}
|
||||
}
|
||||
|
||||
"not serialize ActorCell" in {
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case o: ObjectOutputStream ⇒
|
||||
try {
|
||||
o.writeObject(this)
|
||||
} catch {
|
||||
case _: NotSerializableException ⇒ testActor ! "pass"
|
||||
}
|
||||
}
|
||||
}))
|
||||
a ! new ObjectOutputStream(new ByteArrayOutputStream())
|
||||
expectMsg("pass")
|
||||
a.stop()
|
||||
}
|
||||
|
||||
"serialize DeadLetterActorRef" in {
|
||||
val outbuf = new ByteArrayOutputStream()
|
||||
val out = new ObjectOutputStream(outbuf)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,16 @@ class DurationSpec extends WordSpec with MustMatchers {
|
|||
(minf + minf) must be(minf)
|
||||
}
|
||||
|
||||
"support fromNow" in {
|
||||
val dead = 2.seconds.fromNow
|
||||
val dead2 = 2 seconds fromNow
|
||||
dead.timeLeft must be > 1.second
|
||||
dead2.timeLeft must be > 1.second
|
||||
1.second.sleep
|
||||
dead.timeLeft must be < 1.second
|
||||
dead2.timeLeft must be < 1.second
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
package com.typesafe.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An immutable map from config paths to config values.
|
||||
|
|
@ -32,6 +34,10 @@ import java.util.List;
|
|||
* {@code ConfigObject} is a tree of nested maps from <em>keys</em> to values.
|
||||
*
|
||||
* <p>
|
||||
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||
* between path expressions and individual path elements (keys).
|
||||
*
|
||||
* <p>
|
||||
* Another difference between {@code Config} and {@code ConfigObject} is that
|
||||
* conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
|
||||
* valueType()} of {@link ConfigValueType#NULL NULL} exist in a
|
||||
|
|
@ -54,10 +60,11 @@ import java.util.List;
|
|||
* are performed for you though.
|
||||
*
|
||||
* <p>
|
||||
* If you want to iterate over the contents of a {@code Config}, you have to get
|
||||
* its {@code ConfigObject} with {@link #root()}, and then iterate over the
|
||||
* {@code ConfigObject}.
|
||||
*
|
||||
* If you want to iterate over the contents of a {@code Config}, you can get its
|
||||
* {@code ConfigObject} with {@link #root()}, and then iterate over the
|
||||
* {@code ConfigObject} (which implements <code>java.util.Map</code>). Or, you
|
||||
* can use {@link #entrySet()} which recurses the object tree for you and builds
|
||||
* up a <code>Set</code> of all path-value pairs where the value is not null.
|
||||
*
|
||||
* <p>
|
||||
* <em>Do not implement {@code Config}</em>; it should only be implemented by
|
||||
|
|
@ -256,6 +263,17 @@ public interface Config extends ConfigMergeable {
|
|||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the set of path-value pairs, excluding any null values, found by
|
||||
* recursing {@link #root() the root object}. Note that this is very
|
||||
* different from <code>root().entrySet()</code> which returns the set of
|
||||
* immediate-child keys in the root object and includes null values.
|
||||
*
|
||||
* @return set of paths with non-null values, built up by recursing the
|
||||
* entire tree of {@link ConfigObject}
|
||||
*/
|
||||
Set<Map.Entry<String, ConfigValue>> entrySet();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import java.util.Map;
|
|||
import java.util.Properties;
|
||||
|
||||
import com.typesafe.config.impl.ConfigImpl;
|
||||
import com.typesafe.config.impl.ConfigUtil;
|
||||
import com.typesafe.config.impl.ConfigImplUtil;
|
||||
import com.typesafe.config.impl.Parseable;
|
||||
|
||||
/**
|
||||
|
|
@ -179,7 +179,7 @@ public final class ConfigFactory {
|
|||
try {
|
||||
return DefaultConfigHolder.defaultConfig;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigUtil.extractInitializerError(e);
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ import java.util.Map;
|
|||
* {@code ConfigObject} is a one-level map from paths to values.
|
||||
*
|
||||
* <p>
|
||||
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
|
||||
* between path expressions and individual path elements (keys).
|
||||
*
|
||||
* <p>
|
||||
* A {@code ConfigObject} may contain null values, which will have
|
||||
* {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If
|
||||
* {@code get()} returns Java's null then the key was not present in the parsed
|
||||
|
|
|
|||
70
akka-actor/src/main/java/com/typesafe/config/ConfigUtil.java
Normal file
70
akka-actor/src/main/java/com/typesafe/config/ConfigUtil.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package com.typesafe.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.impl.ConfigImplUtil;
|
||||
|
||||
public final class ConfigUtil {
|
||||
private ConfigUtil() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes and escapes a string, as in the JSON specification.
|
||||
*
|
||||
* @param s
|
||||
* a string
|
||||
* @return the string quoted and escaped
|
||||
*/
|
||||
public static String quoteString(String s) {
|
||||
return ConfigImplUtil.renderJsonString(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of keys to a path expression, by quoting the path
|
||||
* elements as needed and then joining them separated by a period. A path
|
||||
* expression is usable with a {@link Config}, while individual path
|
||||
* elements are usable with a {@link ConfigObject}.
|
||||
*
|
||||
* @param elements
|
||||
* the keys in the path
|
||||
* @return a path expression
|
||||
* @throws ConfigException
|
||||
* if there are no elements
|
||||
*/
|
||||
public static String joinPath(String... elements) {
|
||||
return ConfigImplUtil.joinPath(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of strings to a path expression, by quoting the path
|
||||
* elements as needed and then joining them separated by a period. A path
|
||||
* expression is usable with a {@link Config}, while individual path
|
||||
* elements are usable with a {@link ConfigObject}.
|
||||
*
|
||||
* @param elements
|
||||
* the keys in the path
|
||||
* @return a path expression
|
||||
* @throws ConfigException
|
||||
* if the list is empty
|
||||
*/
|
||||
public static String joinPath(List<String> elements) {
|
||||
return ConfigImplUtil.joinPath(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a path expression into a list of keys, by splitting on period
|
||||
* and unquoting the individual path elements. A path expression is usable
|
||||
* with a {@link Config}, while individual path elements are usable with a
|
||||
* {@link ConfigObject}.
|
||||
*
|
||||
* @param path
|
||||
* a path expression
|
||||
* @return the individual keys in the path
|
||||
* @throws ConfigException
|
||||
* if the path expression is invalid
|
||||
*/
|
||||
public static List<String> splitPath(String path) {
|
||||
return ConfigImplUtil.splitPath(path);
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
return canEqual(other)
|
||||
&& (this.valueType() ==
|
||||
((ConfigValue) other).valueType())
|
||||
&& ConfigUtil.equalsHandlingNull(this.unwrapped(),
|
||||
&& ConfigImplUtil.equalsHandlingNull(this.unwrapped(),
|
||||
((ConfigValue) other).unwrapped());
|
||||
} else {
|
||||
return false;
|
||||
|
|
@ -178,7 +178,7 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
|
|||
|
||||
protected void render(StringBuilder sb, int indent, String atKey, boolean formatted) {
|
||||
if (atKey != null) {
|
||||
sb.append(ConfigUtil.renderJsonString(atKey));
|
||||
sb.append(ConfigImplUtil.renderJsonString(atKey));
|
||||
sb.append(" : ");
|
||||
}
|
||||
render(sb, indent, formatted);
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
|||
indent(sb, indent);
|
||||
if (atKey != null) {
|
||||
sb.append("# unmerged value " + i + " for key "
|
||||
+ ConfigUtil.renderJsonString(atKey) + " from ");
|
||||
+ ConfigImplUtil.renderJsonString(atKey) + " from ");
|
||||
} else {
|
||||
sb.append("# unmerged value " + i + " from ");
|
||||
}
|
||||
|
|
@ -200,7 +200,7 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
|
|||
}
|
||||
|
||||
if (atKey != null) {
|
||||
sb.append(ConfigUtil.renderJsonString(atKey));
|
||||
sb.append(ConfigImplUtil.renderJsonString(atKey));
|
||||
sb.append(" : ");
|
||||
}
|
||||
v.render(sb, indent, formatted);
|
||||
|
|
|
|||
|
|
@ -40,47 +40,66 @@ public class ConfigImpl {
|
|||
|| name.endsWith(".properties")) {
|
||||
ConfigParseable p = source.nameToParseable(name);
|
||||
|
||||
if (p != null) {
|
||||
obj = p.parse(p.options().setAllowMissing(
|
||||
options.getAllowMissing()));
|
||||
} else {
|
||||
obj = SimpleConfigObject.emptyMissing(SimpleConfigOrigin.newSimple(name));
|
||||
}
|
||||
obj = p.parse(p.options().setAllowMissing(options.getAllowMissing()));
|
||||
} else {
|
||||
ConfigParseable confHandle = source.nameToParseable(name + ".conf");
|
||||
ConfigParseable jsonHandle = source.nameToParseable(name + ".json");
|
||||
ConfigParseable propsHandle = source.nameToParseable(name
|
||||
+ ".properties");
|
||||
|
||||
if (!options.getAllowMissing() && confHandle == null
|
||||
&& jsonHandle == null && propsHandle == null) {
|
||||
throw new ConfigException.IO(SimpleConfigOrigin.newSimple(name),
|
||||
"No config files {.conf,.json,.properties} found");
|
||||
}
|
||||
boolean gotSomething = false;
|
||||
List<String> failMessages = new ArrayList<String>();
|
||||
|
||||
ConfigSyntax syntax = options.getSyntax();
|
||||
|
||||
obj = SimpleConfigObject.empty(SimpleConfigOrigin.newSimple(name));
|
||||
if (confHandle != null
|
||||
&& (syntax == null || syntax == ConfigSyntax.CONF)) {
|
||||
obj = confHandle.parse(confHandle.options()
|
||||
.setAllowMissing(true).setSyntax(ConfigSyntax.CONF));
|
||||
if (syntax == null || syntax == ConfigSyntax.CONF) {
|
||||
try {
|
||||
obj = confHandle.parse(confHandle.options().setAllowMissing(false)
|
||||
.setSyntax(ConfigSyntax.CONF));
|
||||
gotSomething = true;
|
||||
} catch (ConfigException.IO e) {
|
||||
failMessages.add(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonHandle != null
|
||||
&& (syntax == null || syntax == ConfigSyntax.JSON)) {
|
||||
ConfigObject parsed = jsonHandle.parse(jsonHandle
|
||||
.options().setAllowMissing(true)
|
||||
.setSyntax(ConfigSyntax.JSON));
|
||||
if (syntax == null || syntax == ConfigSyntax.JSON) {
|
||||
try {
|
||||
ConfigObject parsed = jsonHandle.parse(jsonHandle.options()
|
||||
.setAllowMissing(false).setSyntax(ConfigSyntax.JSON));
|
||||
obj = obj.withFallback(parsed);
|
||||
gotSomething = true;
|
||||
} catch (ConfigException.IO e) {
|
||||
failMessages.add(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (propsHandle != null
|
||||
&& (syntax == null || syntax == ConfigSyntax.PROPERTIES)) {
|
||||
if (syntax == null || syntax == ConfigSyntax.PROPERTIES) {
|
||||
try {
|
||||
ConfigObject parsed = propsHandle.parse(propsHandle.options()
|
||||
.setAllowMissing(true)
|
||||
.setSyntax(ConfigSyntax.PROPERTIES));
|
||||
.setAllowMissing(false).setSyntax(ConfigSyntax.PROPERTIES));
|
||||
obj = obj.withFallback(parsed);
|
||||
gotSomething = true;
|
||||
} catch (ConfigException.IO e) {
|
||||
failMessages.add(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.getAllowMissing() && !gotSomething) {
|
||||
String failMessage;
|
||||
if (failMessages.isEmpty()) {
|
||||
// this should not happen
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"should not be reached: nothing found but no exceptions thrown");
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String msg : failMessages) {
|
||||
sb.append(msg);
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.setLength(sb.length() - 2);
|
||||
failMessage = sb.toString();
|
||||
}
|
||||
throw new ConfigException.IO(SimpleConfigOrigin.newSimple(name), failMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +288,14 @@ public class ConfigImpl {
|
|||
NameSource source = new NameSource() {
|
||||
@Override
|
||||
public ConfigParseable nameToParseable(String name) {
|
||||
return context.relativeTo(name);
|
||||
ConfigParseable p = context.relativeTo(name);
|
||||
if (p == null) {
|
||||
// avoid returning null
|
||||
return Parseable.newNotFound(name, "include was not found: '" + name + "'",
|
||||
ConfigParseOptions.defaults());
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -308,7 +334,7 @@ public class ConfigImpl {
|
|||
try {
|
||||
return DefaultIncluderHolder.defaultIncluder;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigUtil.extractInitializerError(e);
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +352,7 @@ public class ConfigImpl {
|
|||
try {
|
||||
return SystemPropertiesHolder.systemProperties;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigUtil.extractInitializerError(e);
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +388,7 @@ public class ConfigImpl {
|
|||
try {
|
||||
return EnvVariablesHolder.envVariables;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigUtil.extractInitializerError(e);
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,7 +410,7 @@ public class ConfigImpl {
|
|||
try {
|
||||
return ReferenceHolder.referenceConfig;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigUtil.extractInitializerError(e);
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ package com.typesafe.config.impl;
|
|||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
|
||||
|
||||
/** This is public just for the "config" package to use, don't touch it */
|
||||
final public class ConfigUtil {
|
||||
final public class ConfigImplUtil {
|
||||
static boolean equalsHandlingNull(Object a, Object b) {
|
||||
if (a == null && b != null)
|
||||
return false;
|
||||
|
|
@ -23,7 +25,11 @@ final public class ConfigUtil {
|
|||
return a.equals(b);
|
||||
}
|
||||
|
||||
static String renderJsonString(String s) {
|
||||
/**
|
||||
* This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
||||
* may change.
|
||||
*/
|
||||
public static String renderJsonString(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
|
|
@ -146,4 +152,34 @@ final public class ConfigUtil {
|
|||
return new File(url.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
||||
* may change. You can use the version in ConfigUtil instead.
|
||||
*/
|
||||
public static String joinPath(String... elements) {
|
||||
return (new Path(elements)).render();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
||||
* may change. You can use the version in ConfigUtil instead.
|
||||
*/
|
||||
public static String joinPath(List<String> elements) {
|
||||
return joinPath(elements.toArray(new String[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
||||
* may change. You can use the version in ConfigUtil instead.
|
||||
*/
|
||||
public static List<String> splitPath(String path) {
|
||||
Path p = Path.newPath(path);
|
||||
List<String> elements = new ArrayList<String>();
|
||||
while (p != null) {
|
||||
elements.add(p.first());
|
||||
p = p.remainder();
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,6 @@ final class ConfigString extends AbstractConfigValue {
|
|||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
sb.append(ConfigUtil.renderJsonString(value));
|
||||
sb.append(ConfigImplUtil.renderJsonString(value));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
|||
if (p instanceof SubstitutionExpression) {
|
||||
sb.append(p.toString());
|
||||
} else {
|
||||
sb.append(ConfigUtil.renderJsonString((String) p));
|
||||
sb.append(ConfigImplUtil.renderJsonString((String) p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package com.typesafe.config.impl;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FilterReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
|
@ -261,6 +262,34 @@ public abstract class Parseable implements ConfigParseable {
|
|||
return new File(parent, filename);
|
||||
}
|
||||
|
||||
// this is a parseable that doesn't exist and just throws when you try to
|
||||
// parse it
|
||||
private final static class ParseableNotFound extends Parseable {
|
||||
final private String what;
|
||||
final private String message;
|
||||
|
||||
ParseableNotFound(String what, String message, ConfigParseOptions options) {
|
||||
this.what = what;
|
||||
this.message = message;
|
||||
postConstruct(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader reader() throws IOException {
|
||||
throw new FileNotFoundException(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigOrigin createOrigin() {
|
||||
return SimpleConfigOrigin.newSimple(what);
|
||||
}
|
||||
}
|
||||
|
||||
public static Parseable newNotFound(String whatNotFound, String message,
|
||||
ConfigParseOptions options) {
|
||||
return new ParseableNotFound(whatNotFound, message, options);
|
||||
}
|
||||
|
||||
private final static class ParseableReader extends Parseable {
|
||||
final private Reader reader;
|
||||
|
||||
|
|
@ -355,7 +384,7 @@ public abstract class Parseable implements ConfigParseable {
|
|||
// we want file: URLs and files to always behave the same, so switch
|
||||
// to a file if it's a file: URL
|
||||
if (input.getProtocol().equals("file")) {
|
||||
return newFile(ConfigUtil.urlToFile(input), options);
|
||||
return newFile(ConfigImplUtil.urlToFile(input), options);
|
||||
} else {
|
||||
return new ParseableURL(input, options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ final class Parser {
|
|||
final private ConfigSyntax flavor;
|
||||
final private ConfigOrigin baseOrigin;
|
||||
final private LinkedList<Path> pathStack;
|
||||
// this is the number of "equals" we are inside,
|
||||
// used to modify the error message to reflect that
|
||||
// someone may think this is .properties format.
|
||||
int equalsCount;
|
||||
|
||||
ParseContext(ConfigSyntax flavor, ConfigOrigin origin,
|
||||
Iterator<Token> tokens, ConfigIncluder includer,
|
||||
|
|
@ -53,6 +57,7 @@ final class Parser {
|
|||
this.includer = includer;
|
||||
this.includeContext = includeContext;
|
||||
this.pathStack = new LinkedList<Path>();
|
||||
this.equalsCount = 0;
|
||||
}
|
||||
|
||||
private Token nextToken() {
|
||||
|
|
@ -63,12 +68,25 @@ final class Parser {
|
|||
t = buffer.pop();
|
||||
}
|
||||
|
||||
if (Tokens.isProblem(t)) {
|
||||
ConfigOrigin origin = t.origin();
|
||||
String message = Tokens.getProblemMessage(t);
|
||||
Throwable cause = Tokens.getProblemCause(t);
|
||||
boolean suggestQuotes = Tokens.getProblemSuggestQuotes(t);
|
||||
if (suggestQuotes) {
|
||||
message = addQuoteSuggestion(t.toString(), message);
|
||||
} else {
|
||||
message = addKeyName(message);
|
||||
}
|
||||
throw new ConfigException.Parse(origin, message, cause);
|
||||
}
|
||||
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
if (Tokens.isUnquotedText(t)) {
|
||||
throw parseError("Token not allowed in valid JSON: '"
|
||||
+ Tokens.getUnquotedText(t) + "'");
|
||||
throw parseError(addKeyName("Token not allowed in valid JSON: '"
|
||||
+ Tokens.getUnquotedText(t) + "'"));
|
||||
} else if (Tokens.isSubstitution(t)) {
|
||||
throw parseError("Substitutions (${} syntax) not allowed in JSON");
|
||||
throw parseError(addKeyName("Substitutions (${} syntax) not allowed in JSON"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +102,7 @@ final class Parser {
|
|||
while (Tokens.isNewline(t)) {
|
||||
// line number tokens have the line that was _ended_ by the
|
||||
// newline, so we have to add one.
|
||||
lineNumber = Tokens.getLineNumber(t) + 1;
|
||||
lineNumber = t.lineNumber() + 1;
|
||||
t = nextToken();
|
||||
}
|
||||
return t;
|
||||
|
|
@ -111,7 +129,7 @@ final class Parser {
|
|||
while (true) {
|
||||
if (Tokens.isNewline(t)) {
|
||||
// newline number is the line just ended, so add one
|
||||
lineNumber = Tokens.getLineNumber(t) + 1;
|
||||
lineNumber = t.lineNumber() + 1;
|
||||
sawSeparatorOrNewline = true;
|
||||
// we want to continue to also eat
|
||||
// a comma if there is one.
|
||||
|
|
@ -172,11 +190,11 @@ final class Parser {
|
|||
} else if (Tokens.isUnquotedText(valueToken)) {
|
||||
String text = Tokens.getUnquotedText(valueToken);
|
||||
if (firstOrigin == null)
|
||||
firstOrigin = Tokens.getUnquotedTextOrigin(valueToken);
|
||||
firstOrigin = valueToken.origin();
|
||||
sb.append(text);
|
||||
} else if (Tokens.isSubstitution(valueToken)) {
|
||||
if (firstOrigin == null)
|
||||
firstOrigin = Tokens.getSubstitutionOrigin(valueToken);
|
||||
firstOrigin = valueToken.origin();
|
||||
|
||||
if (sb.length() > 0) {
|
||||
// save string so far
|
||||
|
|
@ -186,8 +204,7 @@ final class Parser {
|
|||
// now save substitution
|
||||
List<Token> expression = Tokens
|
||||
.getSubstitutionPathExpression(valueToken);
|
||||
Path path = parsePathExpression(expression.iterator(),
|
||||
Tokens.getSubstitutionOrigin(valueToken));
|
||||
Path path = parsePathExpression(expression.iterator(), valueToken.origin());
|
||||
boolean optional = Tokens.getSubstitutionOptional(valueToken);
|
||||
|
||||
minimized.add(new SubstitutionExpression(path, optional));
|
||||
|
|
@ -233,6 +250,65 @@ final class Parser {
|
|||
return new ConfigException.Parse(lineOrigin(), message, cause);
|
||||
}
|
||||
|
||||
|
||||
private String previousFieldName(Path lastPath) {
|
||||
if (lastPath != null) {
|
||||
return lastPath.render();
|
||||
} else if (pathStack.isEmpty())
|
||||
return null;
|
||||
else
|
||||
return pathStack.peek().render();
|
||||
}
|
||||
|
||||
private String previousFieldName() {
|
||||
return previousFieldName(null);
|
||||
}
|
||||
|
||||
private String addKeyName(String message) {
|
||||
String previousFieldName = previousFieldName();
|
||||
if (previousFieldName != null) {
|
||||
return "in value for key '" + previousFieldName + "': " + message;
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
private String addQuoteSuggestion(String badToken, String message) {
|
||||
return addQuoteSuggestion(null, equalsCount > 0, badToken, message);
|
||||
}
|
||||
|
||||
private String addQuoteSuggestion(Path lastPath, boolean insideEquals, String badToken,
|
||||
String message) {
|
||||
String previousFieldName = previousFieldName(lastPath);
|
||||
|
||||
String part;
|
||||
if (badToken.equals(Tokens.END.toString())) {
|
||||
// EOF requires special handling for the error to make sense.
|
||||
if (previousFieldName != null)
|
||||
part = message + " (if you intended '" + previousFieldName
|
||||
+ "' to be part of a value, instead of a key, "
|
||||
+ "try adding double quotes around the whole value";
|
||||
else
|
||||
return message;
|
||||
} else {
|
||||
if (previousFieldName != null) {
|
||||
part = message + " (if you intended " + badToken
|
||||
+ " to be part of the value for '" + previousFieldName + "', "
|
||||
+ "try enclosing the value in double quotes";
|
||||
} else {
|
||||
part = message + " (if you intended " + badToken
|
||||
+ " to be part of a key or string value, "
|
||||
+ "try enclosing the key or value in double quotes";
|
||||
}
|
||||
}
|
||||
|
||||
if (insideEquals)
|
||||
return part
|
||||
+ ", or you may be able to rename the file .properties rather than .conf)";
|
||||
else
|
||||
return part + ")";
|
||||
}
|
||||
|
||||
private AbstractConfigValue parseValue(Token token) {
|
||||
if (Tokens.isValue(token)) {
|
||||
return Tokens.getValue(token);
|
||||
|
|
@ -241,8 +317,8 @@ final class Parser {
|
|||
} else if (token == Tokens.OPEN_SQUARE) {
|
||||
return parseArray();
|
||||
} else {
|
||||
throw parseError("Expecting a value but got wrong token: "
|
||||
+ token);
|
||||
throw parseError(addQuoteSuggestion(token.toString(),
|
||||
"Expecting a value but got wrong token: " + token));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -283,8 +359,8 @@ final class Parser {
|
|||
String key = (String) Tokens.getValue(token).unwrapped();
|
||||
return Path.newKey(key);
|
||||
} else {
|
||||
throw parseError("Expecting close brace } or a field name, got "
|
||||
+ token);
|
||||
throw parseError(addKeyName("Expecting close brace } or a field name here, got "
|
||||
+ token));
|
||||
}
|
||||
} else {
|
||||
List<Token> expression = new ArrayList<Token>();
|
||||
|
|
@ -293,6 +369,12 @@ final class Parser {
|
|||
expression.add(t);
|
||||
t = nextToken(); // note: don't cross a newline
|
||||
}
|
||||
|
||||
if (expression.isEmpty()) {
|
||||
throw parseError(addKeyName("expecting a close brace or a field name here, got "
|
||||
+ t));
|
||||
}
|
||||
|
||||
putBack(t); // put back the token we ended with
|
||||
return parsePathExpression(expression.iterator(), lineOrigin());
|
||||
}
|
||||
|
|
@ -311,7 +393,7 @@ final class Parser {
|
|||
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
char c = s.charAt(i);
|
||||
if (!ConfigUtil.isWhitespace(c))
|
||||
if (!ConfigImplUtil.isWhitespace(c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -362,13 +444,18 @@ final class Parser {
|
|||
Map<String, AbstractConfigValue> values = new HashMap<String, AbstractConfigValue>();
|
||||
ConfigOrigin objectOrigin = lineOrigin();
|
||||
boolean afterComma = false;
|
||||
Path lastPath = null;
|
||||
boolean lastInsideEquals = false;
|
||||
|
||||
while (true) {
|
||||
Token t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.CLOSE_CURLY) {
|
||||
if (flavor == ConfigSyntax.JSON && afterComma) {
|
||||
throw parseError("expecting a field name after comma, got a close brace }");
|
||||
throw parseError(addQuoteSuggestion(t.toString(),
|
||||
"expecting a field name after a comma, got a close brace } instead"));
|
||||
} else if (!hadOpenCurly) {
|
||||
throw parseError("unbalanced close brace '}' with no open brace");
|
||||
throw parseError(addQuoteSuggestion(t.toString(),
|
||||
"unbalanced close brace '}' with no open brace"));
|
||||
}
|
||||
break;
|
||||
} else if (t == Tokens.END && !hadOpenCurly) {
|
||||
|
|
@ -381,6 +468,7 @@ final class Parser {
|
|||
} else {
|
||||
Path path = parseKey(t);
|
||||
Token afterKey = nextTokenIgnoringNewline();
|
||||
boolean insideEquals = false;
|
||||
|
||||
// path must be on-stack while we parse the value
|
||||
pathStack.push(path);
|
||||
|
|
@ -394,8 +482,14 @@ final class Parser {
|
|||
newValue = parseObject(true);
|
||||
} else {
|
||||
if (!isKeyValueSeparatorToken(afterKey)) {
|
||||
throw parseError("Key may not be followed by token: "
|
||||
+ afterKey);
|
||||
throw parseError(addQuoteSuggestion(afterKey.toString(),
|
||||
"Key '" + path.render() + "' may not be followed by token: "
|
||||
+ afterKey));
|
||||
}
|
||||
|
||||
if (afterKey == Tokens.EQUALS) {
|
||||
insideEquals = true;
|
||||
equalsCount += 1;
|
||||
}
|
||||
|
||||
consolidateValueTokens();
|
||||
|
|
@ -403,7 +497,11 @@ final class Parser {
|
|||
newValue = parseValue(valueToken);
|
||||
}
|
||||
|
||||
pathStack.pop();
|
||||
lastPath = pathStack.pop();
|
||||
if (insideEquals) {
|
||||
equalsCount -= 1;
|
||||
}
|
||||
lastInsideEquals = insideEquals;
|
||||
|
||||
String key = path.first();
|
||||
Path remaining = path.remainder();
|
||||
|
|
@ -451,25 +549,25 @@ final class Parser {
|
|||
t = nextTokenIgnoringNewline();
|
||||
if (t == Tokens.CLOSE_CURLY) {
|
||||
if (!hadOpenCurly) {
|
||||
throw parseError("unbalanced close brace '}' with no open brace");
|
||||
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||
t.toString(), "unbalanced close brace '}' with no open brace"));
|
||||
}
|
||||
break;
|
||||
} else if (hadOpenCurly) {
|
||||
throw parseError("Expecting close brace } or a comma, got "
|
||||
+ t);
|
||||
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||
t.toString(), "Expecting close brace } or a comma, got " + t));
|
||||
} else {
|
||||
if (t == Tokens.END) {
|
||||
putBack(t);
|
||||
break;
|
||||
} else {
|
||||
throw parseError("Expecting end of input or a comma, got "
|
||||
+ t);
|
||||
throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals,
|
||||
t.toString(), "Expecting end of input or a comma, got " + t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new SimpleConfigObject(objectOrigin,
|
||||
values);
|
||||
return new SimpleConfigObject(objectOrigin, values);
|
||||
}
|
||||
|
||||
private SimpleConfigList parseArray() {
|
||||
|
|
@ -492,8 +590,11 @@ final class Parser {
|
|||
} else if (t == Tokens.OPEN_SQUARE) {
|
||||
values.add(parseArray());
|
||||
} else {
|
||||
throw parseError("List should have ] or a first element after the open [, instead had token: "
|
||||
+ t);
|
||||
throw parseError(addKeyName("List should have ] or a first element after the open [, instead had token: "
|
||||
+ t
|
||||
+ " (if you want "
|
||||
+ t
|
||||
+ " to be part of a string value, then double-quote it)"));
|
||||
}
|
||||
|
||||
// now remaining elements
|
||||
|
|
@ -506,8 +607,11 @@ final class Parser {
|
|||
if (t == Tokens.CLOSE_SQUARE) {
|
||||
return new SimpleConfigList(arrayOrigin, values);
|
||||
} else {
|
||||
throw parseError("List should have ended with ] or had a comma, instead had token: "
|
||||
+ t);
|
||||
throw parseError(addKeyName("List should have ended with ] or had a comma, instead had token: "
|
||||
+ t
|
||||
+ " (if you want "
|
||||
+ t
|
||||
+ " to be part of a string value, then double-quote it)"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,8 +630,11 @@ final class Parser {
|
|||
// we allow one trailing comma
|
||||
putBack(t);
|
||||
} else {
|
||||
throw parseError("List should have had new element after a comma, instead had token: "
|
||||
+ t);
|
||||
throw parseError(addKeyName("List should have had new element after a comma, instead had token: "
|
||||
+ t
|
||||
+ " (if you want the comma or "
|
||||
+ t
|
||||
+ " to be part of a string value, then double-quote it)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -659,9 +766,12 @@ final class Parser {
|
|||
} else if (Tokens.isUnquotedText(t)) {
|
||||
text = Tokens.getUnquotedText(t);
|
||||
} else {
|
||||
throw new ConfigException.BadPath(origin, originalText,
|
||||
throw new ConfigException.BadPath(
|
||||
origin,
|
||||
originalText,
|
||||
"Token not allowed in path expression: "
|
||||
+ t);
|
||||
+ t
|
||||
+ " (you can double-quote this token if you really want it here)");
|
||||
}
|
||||
|
||||
addPathText(buf, false, text);
|
||||
|
|
@ -728,7 +838,7 @@ final class Parser {
|
|||
// do something much faster than the full parser if
|
||||
// we just have something like "foo" or "foo.bar"
|
||||
private static Path speculativeFastParsePath(String path) {
|
||||
String s = ConfigUtil.unicodeTrim(path);
|
||||
String s = ConfigImplUtil.unicodeTrim(path);
|
||||
if (s.isEmpty())
|
||||
return null;
|
||||
if (hasUnsafeChars(s))
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ final class Path {
|
|||
if (other instanceof Path) {
|
||||
Path that = (Path) other;
|
||||
return this.first.equals(that.first)
|
||||
&& ConfigUtil.equalsHandlingNull(this.remainder,
|
||||
&& ConfigImplUtil.equalsHandlingNull(this.remainder,
|
||||
that.remainder);
|
||||
} else {
|
||||
return false;
|
||||
|
|
@ -167,7 +167,7 @@ final class Path {
|
|||
|
||||
private void appendToStringBuilder(StringBuilder sb) {
|
||||
if (hasFunkyChars(first) || first.isEmpty())
|
||||
sb.append(ConfigUtil.renderJsonString(first));
|
||||
sb.append(ConfigImplUtil.renderJsonString(first));
|
||||
else
|
||||
sb.append(first);
|
||||
if (remainder != null) {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
|
|
@ -20,12 +23,10 @@ import com.typesafe.config.ConfigValue;
|
|||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
* One thing to keep in mind in the future: if any Collection-like APIs are
|
||||
* added here, including iterators or size() or anything, then we'd have to
|
||||
* grapple with whether ConfigNull values are "in" the Config (probably not) and
|
||||
* we'd probably want to make the collection look flat - not like a tree. So the
|
||||
* key-value pairs would be all the tree's leaf values, in a big flat list with
|
||||
* their full paths.
|
||||
* One thing to keep in mind in the future: as Collection-like APIs are added
|
||||
* here, including iterators or size() or anything, they should be consistent
|
||||
* with a one-level java.util.Map from paths to non-null values. Null values are
|
||||
* not "in" the map.
|
||||
*/
|
||||
final class SimpleConfig implements Config, MergeableValue {
|
||||
|
||||
|
|
@ -73,6 +74,31 @@ final class SimpleConfig implements Config, MergeableValue {
|
|||
return object.isEmpty();
|
||||
}
|
||||
|
||||
private static void findPaths(Set<Map.Entry<String, ConfigValue>> entries, Path parent,
|
||||
AbstractConfigObject obj) {
|
||||
for (Map.Entry<String, ConfigValue> entry : obj.entrySet()) {
|
||||
String elem = entry.getKey();
|
||||
ConfigValue v = entry.getValue();
|
||||
Path path = Path.newKey(elem);
|
||||
if (parent != null)
|
||||
path = path.prepend(parent);
|
||||
if (v instanceof AbstractConfigObject) {
|
||||
findPaths(entries, path, (AbstractConfigObject) v);
|
||||
} else if (v instanceof ConfigNull) {
|
||||
// nothing; nulls are conceptually not in a Config
|
||||
} else {
|
||||
entries.add(new AbstractMap.SimpleImmutableEntry<String, ConfigValue>(path.render(), v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<String, ConfigValue>> entrySet() {
|
||||
Set<Map.Entry<String, ConfigValue>> entries = new HashSet<Map.Entry<String, ConfigValue>>();
|
||||
findPaths(entries, null, object);
|
||||
return entries;
|
||||
}
|
||||
|
||||
static private AbstractConfigValue find(AbstractConfigObject self,
|
||||
String pathExpression, ConfigValueType expected, String originalPath) {
|
||||
Path path = Path.newPath(pathExpression);
|
||||
|
|
@ -440,10 +466,10 @@ final class SimpleConfig implements Config, MergeableValue {
|
|||
*/
|
||||
public static long parseDuration(String input,
|
||||
ConfigOrigin originForException, String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String s = ConfigImplUtil.unicodeTrim(input);
|
||||
String originalUnitString = getUnits(s);
|
||||
String unitString = originalUnitString;
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0, s.length()
|
||||
String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, s.length()
|
||||
- unitString.length()));
|
||||
TimeUnit units = null;
|
||||
|
||||
|
|
@ -592,9 +618,9 @@ final class SimpleConfig implements Config, MergeableValue {
|
|||
*/
|
||||
public static long parseBytes(String input, ConfigOrigin originForException,
|
||||
String pathForException) {
|
||||
String s = ConfigUtil.unicodeTrim(input);
|
||||
String s = ConfigImplUtil.unicodeTrim(input);
|
||||
String unitString = getUnits(s);
|
||||
String numberString = ConfigUtil.unicodeTrim(s.substring(0,
|
||||
String numberString = ConfigImplUtil.unicodeTrim(s.substring(0,
|
||||
s.length() - unitString.length()));
|
||||
|
||||
// this would be caught later anyway, but the error message
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
&& this.lineNumber == otherOrigin.lineNumber
|
||||
&& this.endLineNumber == otherOrigin.endLineNumber
|
||||
&& this.originType == otherOrigin.originType
|
||||
&& ConfigUtil.equalsHandlingNull(this.urlOrNull, otherOrigin.urlOrNull);
|
||||
&& ConfigImplUtil.equalsHandlingNull(this.urlOrNull, otherOrigin.urlOrNull);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
}
|
||||
|
||||
String mergedURL;
|
||||
if (ConfigUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) {
|
||||
if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) {
|
||||
mergedURL = a.urlOrNull;
|
||||
} else {
|
||||
mergedURL = null;
|
||||
|
|
@ -252,7 +252,7 @@ final class SimpleConfigOrigin implements ConfigOrigin {
|
|||
count += 1;
|
||||
if (a.endLineNumber == b.endLineNumber)
|
||||
count += 1;
|
||||
if (ConfigUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull))
|
||||
if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull))
|
||||
count += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,56 @@
|
|||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
|
||||
class Token {
|
||||
final private TokenType tokenType;
|
||||
final private String debugString;
|
||||
final private ConfigOrigin origin;
|
||||
|
||||
Token(TokenType tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
Token(TokenType tokenType, ConfigOrigin origin) {
|
||||
this(tokenType, origin, null);
|
||||
}
|
||||
|
||||
public TokenType tokenType() {
|
||||
Token(TokenType tokenType, ConfigOrigin origin, String debugString) {
|
||||
this.tokenType = tokenType;
|
||||
this.origin = origin;
|
||||
this.debugString = debugString;
|
||||
}
|
||||
|
||||
// this is used for singleton tokens like COMMA or OPEN_CURLY
|
||||
static Token newWithoutOrigin(TokenType tokenType, String debugString) {
|
||||
return new Token(tokenType, null, debugString);
|
||||
}
|
||||
|
||||
final TokenType tokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
// this is final because we don't always use the origin() accessor,
|
||||
// and we don't because it throws if origin is null
|
||||
final ConfigOrigin origin() {
|
||||
// code is only supposed to call origin() on token types that are
|
||||
// expected to have an origin.
|
||||
if (origin == null)
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"tried to get origin from token that doesn't have one: " + this);
|
||||
return origin;
|
||||
}
|
||||
|
||||
final int lineNumber() {
|
||||
if (origin != null)
|
||||
return origin.lineNumber();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (debugString != null)
|
||||
return debugString;
|
||||
else
|
||||
return tokenType.name();
|
||||
}
|
||||
|
||||
|
|
@ -26,6 +63,7 @@ class Token {
|
|||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Token) {
|
||||
// origin is deliberately left out
|
||||
return canEqual(other)
|
||||
&& this.tokenType == ((Token) other).tokenType;
|
||||
} else {
|
||||
|
|
@ -35,6 +73,7 @@ class Token {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// origin is deliberately left out
|
||||
return tokenType.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,18 @@
|
|||
package com.typesafe.config.impl;
|
||||
|
||||
enum TokenType {
|
||||
START, END, COMMA, EQUALS, COLON, OPEN_CURLY, CLOSE_CURLY, OPEN_SQUARE, CLOSE_SQUARE, VALUE, NEWLINE, UNQUOTED_TEXT, SUBSTITUTION;
|
||||
START,
|
||||
END,
|
||||
COMMA,
|
||||
EQUALS,
|
||||
COLON,
|
||||
OPEN_CURLY,
|
||||
CLOSE_CURLY,
|
||||
OPEN_SQUARE,
|
||||
CLOSE_SQUARE,
|
||||
VALUE,
|
||||
NEWLINE,
|
||||
UNQUOTED_TEXT,
|
||||
SUBSTITUTION,
|
||||
PROBLEM;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,34 @@ import com.typesafe.config.ConfigOrigin;
|
|||
import com.typesafe.config.ConfigSyntax;
|
||||
|
||||
final class Tokenizer {
|
||||
// this exception should not leave this file
|
||||
private static class ProblemException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
final private Token problem;
|
||||
|
||||
ProblemException(Token problem) {
|
||||
this.problem = problem;
|
||||
}
|
||||
|
||||
Token problem() {
|
||||
return problem;
|
||||
}
|
||||
}
|
||||
|
||||
private static String asString(int codepoint) {
|
||||
if (codepoint == '\n')
|
||||
return "newline";
|
||||
else if (codepoint == '\t')
|
||||
return "tab";
|
||||
else if (codepoint == -1)
|
||||
return "end of file";
|
||||
else if (Character.isISOControl(codepoint))
|
||||
return String.format("control character 0x%x", codepoint);
|
||||
else
|
||||
return String.format("%c", codepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a Reader. Does not close the reader; you have to arrange to do
|
||||
* that after you're done with the returned iterator.
|
||||
|
|
@ -85,20 +113,22 @@ final class Tokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
final private ConfigOrigin origin;
|
||||
final private SimpleConfigOrigin origin;
|
||||
final private Reader input;
|
||||
final private LinkedList<Integer> buffer;
|
||||
private int lineNumber;
|
||||
private ConfigOrigin lineOrigin;
|
||||
final private Queue<Token> tokens;
|
||||
final private WhitespaceSaver whitespaceSaver;
|
||||
final private boolean allowComments;
|
||||
|
||||
TokenIterator(ConfigOrigin origin, Reader input, boolean allowComments) {
|
||||
this.origin = origin;
|
||||
this.origin = (SimpleConfigOrigin) origin;
|
||||
this.input = input;
|
||||
this.allowComments = allowComments;
|
||||
this.buffer = new LinkedList<Integer>();
|
||||
lineNumber = 1;
|
||||
lineOrigin = this.origin.setLineNumber(lineNumber);
|
||||
tokens = new LinkedList<Token>();
|
||||
tokens.add(Tokens.START);
|
||||
whitespaceSaver = new WhitespaceSaver();
|
||||
|
|
@ -131,11 +161,11 @@ final class Tokenizer {
|
|||
}
|
||||
|
||||
static boolean isWhitespace(int c) {
|
||||
return ConfigUtil.isWhitespace(c);
|
||||
return ConfigImplUtil.isWhitespace(c);
|
||||
}
|
||||
|
||||
static boolean isWhitespaceNotNewline(int c) {
|
||||
return c != '\n' && ConfigUtil.isWhitespace(c);
|
||||
return c != '\n' && ConfigImplUtil.isWhitespace(c);
|
||||
}
|
||||
|
||||
private int slurpComment() {
|
||||
|
|
@ -194,27 +224,44 @@ final class Tokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
private ConfigException parseError(String message) {
|
||||
return parseError(message, null);
|
||||
private ProblemException problem(String message) {
|
||||
return problem("", message, null);
|
||||
}
|
||||
|
||||
private ConfigException parseError(String message, Throwable cause) {
|
||||
return parseError(lineOrigin(), message, cause);
|
||||
private ProblemException problem(String what, String message) {
|
||||
return problem(what, message, null);
|
||||
}
|
||||
|
||||
private static ConfigException parseError(ConfigOrigin origin,
|
||||
private ProblemException problem(String what, String message, boolean suggestQuotes) {
|
||||
return problem(what, message, suggestQuotes, null);
|
||||
}
|
||||
|
||||
private ProblemException problem(String what, String message, Throwable cause) {
|
||||
return problem(lineOrigin, what, message, cause);
|
||||
}
|
||||
|
||||
private ProblemException problem(String what, String message, boolean suggestQuotes,
|
||||
Throwable cause) {
|
||||
return problem(lineOrigin, what, message, suggestQuotes, cause);
|
||||
}
|
||||
|
||||
private static ProblemException problem(ConfigOrigin origin, String what,
|
||||
String message,
|
||||
Throwable cause) {
|
||||
return new ConfigException.Parse(origin, message, cause);
|
||||
return problem(origin, what, message, false, cause);
|
||||
}
|
||||
|
||||
private static ConfigException parseError(ConfigOrigin origin,
|
||||
String message) {
|
||||
return parseError(origin, message, null);
|
||||
private static ProblemException problem(ConfigOrigin origin, String what, String message,
|
||||
boolean suggestQuotes, Throwable cause) {
|
||||
if (what == null || message == null)
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"internal error, creating bad ProblemException");
|
||||
return new ProblemException(Tokens.newProblem(origin, what, message, suggestQuotes,
|
||||
cause));
|
||||
}
|
||||
|
||||
private ConfigOrigin lineOrigin() {
|
||||
return lineOrigin(origin, lineNumber);
|
||||
private static ProblemException problem(ConfigOrigin origin, String message) {
|
||||
return problem(origin, "", message, null);
|
||||
}
|
||||
|
||||
private static ConfigOrigin lineOrigin(ConfigOrigin baseOrigin,
|
||||
|
|
@ -234,7 +281,7 @@ final class Tokenizer {
|
|||
// that parses as JSON is treated the JSON way and otherwise
|
||||
// we assume it's a string and let the parser sort it out.
|
||||
private Token pullUnquotedText() {
|
||||
ConfigOrigin origin = lineOrigin();
|
||||
ConfigOrigin origin = lineOrigin;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c = nextCharSkippingComments();
|
||||
while (true) {
|
||||
|
|
@ -273,7 +320,7 @@ final class Tokenizer {
|
|||
return Tokens.newUnquotedText(origin, s);
|
||||
}
|
||||
|
||||
private Token pullNumber(int firstChar) {
|
||||
private Token pullNumber(int firstChar) throws ProblemException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.appendCodePoint(firstChar);
|
||||
boolean containedDecimalOrE = false;
|
||||
|
|
@ -291,23 +338,20 @@ final class Tokenizer {
|
|||
try {
|
||||
if (containedDecimalOrE) {
|
||||
// force floating point representation
|
||||
return Tokens.newDouble(lineOrigin(),
|
||||
Double.parseDouble(s), s);
|
||||
return Tokens.newDouble(lineOrigin, Double.parseDouble(s), s);
|
||||
} else {
|
||||
// this should throw if the integer is too large for Long
|
||||
return Tokens.newLong(lineOrigin(), Long.parseLong(s), s);
|
||||
return Tokens.newLong(lineOrigin, Long.parseLong(s), s);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw parseError("Invalid number: '" + s
|
||||
+ "' (if this is in a path, try quoting it with double quotes)",
|
||||
e);
|
||||
throw problem(s, "Invalid number: '" + s + "'", true /* suggestQuotes */, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void pullEscapeSequence(StringBuilder sb) {
|
||||
private void pullEscapeSequence(StringBuilder sb) throws ProblemException {
|
||||
int escaped = nextCharRaw();
|
||||
if (escaped == -1)
|
||||
throw parseError("End of input but backslash in string had nothing after it");
|
||||
throw problem("End of input but backslash in string had nothing after it");
|
||||
|
||||
switch (escaped) {
|
||||
case '"':
|
||||
|
|
@ -340,67 +384,57 @@ final class Tokenizer {
|
|||
for (int i = 0; i < 4; ++i) {
|
||||
int c = nextCharSkippingComments();
|
||||
if (c == -1)
|
||||
throw parseError("End of input but expecting 4 hex digits for \\uXXXX escape");
|
||||
throw problem("End of input but expecting 4 hex digits for \\uXXXX escape");
|
||||
a[i] = (char) c;
|
||||
}
|
||||
String digits = new String(a);
|
||||
try {
|
||||
sb.appendCodePoint(Integer.parseInt(digits, 16));
|
||||
} catch (NumberFormatException e) {
|
||||
throw parseError(
|
||||
String.format(
|
||||
"Malformed hex digits after \\u escape in string: '%s'",
|
||||
digits), e);
|
||||
throw problem(digits, String.format(
|
||||
"Malformed hex digits after \\u escape in string: '%s'", digits), e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw parseError(String
|
||||
.format("backslash followed by '%c', this is not a valid escape sequence",
|
||||
escaped));
|
||||
throw problem(
|
||||
asString(escaped),
|
||||
String.format(
|
||||
"backslash followed by '%s', this is not a valid escape sequence (quoted strings use JSON escaping, so use double-backslash \\\\ for literal backslash)",
|
||||
asString(escaped)));
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigException controlCharacterError(int c) {
|
||||
String asString;
|
||||
if (c == '\n')
|
||||
asString = "newline";
|
||||
else if (c == '\t')
|
||||
asString = "tab";
|
||||
else
|
||||
asString = String.format("control character 0x%x", c);
|
||||
return parseError("JSON does not allow unescaped " + asString
|
||||
+ " in quoted strings, use a backslash escape");
|
||||
}
|
||||
|
||||
private Token pullQuotedString() {
|
||||
private Token pullQuotedString() throws ProblemException {
|
||||
// the open quote has already been consumed
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c = '\0'; // value doesn't get used
|
||||
do {
|
||||
c = nextCharRaw();
|
||||
if (c == -1)
|
||||
throw parseError("End of input but string quote was still open");
|
||||
throw problem("End of input but string quote was still open");
|
||||
|
||||
if (c == '\\') {
|
||||
pullEscapeSequence(sb);
|
||||
} else if (c == '"') {
|
||||
// end the loop, done!
|
||||
} else if (Character.isISOControl(c)) {
|
||||
throw controlCharacterError(c);
|
||||
throw problem(asString(c), "JSON does not allow unescaped " + asString(c)
|
||||
+ " in quoted strings, use a backslash escape");
|
||||
} else {
|
||||
sb.appendCodePoint(c);
|
||||
}
|
||||
} while (c != '"');
|
||||
return Tokens.newString(lineOrigin(), sb.toString());
|
||||
return Tokens.newString(lineOrigin, sb.toString());
|
||||
}
|
||||
|
||||
private Token pullSubstitution() {
|
||||
private Token pullSubstitution() throws ProblemException {
|
||||
// the initial '$' has already been consumed
|
||||
ConfigOrigin origin = lineOrigin();
|
||||
ConfigOrigin origin = lineOrigin;
|
||||
int c = nextCharSkippingComments();
|
||||
if (c != '{') {
|
||||
throw parseError("'$' not followed by {");
|
||||
throw problem(asString(c), "'$' not followed by {, '" + asString(c)
|
||||
+ "' not allowed after '$'", true /* suggestQuotes */);
|
||||
}
|
||||
|
||||
boolean optional = false;
|
||||
|
|
@ -425,7 +459,7 @@ final class Tokenizer {
|
|||
// end the loop, done!
|
||||
break;
|
||||
} else if (t == Tokens.END) {
|
||||
throw parseError(origin,
|
||||
throw problem(origin,
|
||||
"Substitution ${ was not closed with a }");
|
||||
} else {
|
||||
Token whitespace = saver.check(t, origin, lineNumber);
|
||||
|
|
@ -438,14 +472,16 @@ final class Tokenizer {
|
|||
return Tokens.newSubstitution(origin, optional, expression);
|
||||
}
|
||||
|
||||
private Token pullNextToken(WhitespaceSaver saver) {
|
||||
private Token pullNextToken(WhitespaceSaver saver) throws ProblemException {
|
||||
int c = nextCharAfterWhitespace(saver);
|
||||
if (c == -1) {
|
||||
return Tokens.END;
|
||||
} else if (c == '\n') {
|
||||
// newline tokens have the just-ended line number
|
||||
Token line = Tokens.newLine(lineOrigin);
|
||||
lineNumber += 1;
|
||||
return Tokens.newLine(lineNumber - 1);
|
||||
lineOrigin = origin.setLineNumber(lineNumber);
|
||||
return line;
|
||||
} else {
|
||||
Token t = null;
|
||||
switch (c) {
|
||||
|
|
@ -482,9 +518,8 @@ final class Tokenizer {
|
|||
if (firstNumberChars.indexOf(c) >= 0) {
|
||||
t = pullNumber(c);
|
||||
} else if (notInUnquotedText.indexOf(c) >= 0) {
|
||||
throw parseError(String
|
||||
.format("Character '%c' is not the start of any valid token",
|
||||
c));
|
||||
throw problem(asString(c), "Reserved character '" + asString(c)
|
||||
+ "' is not allowed outside quotes", true /* suggestQuotes */);
|
||||
} else {
|
||||
putBack(c);
|
||||
t = pullUnquotedText();
|
||||
|
|
@ -508,7 +543,7 @@ final class Tokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
private void queueNextToken() {
|
||||
private void queueNextToken() throws ProblemException {
|
||||
Token t = pullNextToken(whitespaceSaver);
|
||||
Token whitespace = whitespaceSaver.check(t, origin, lineNumber);
|
||||
if (whitespace != null)
|
||||
|
|
@ -525,7 +560,11 @@ final class Tokenizer {
|
|||
public Token next() {
|
||||
Token t = tokens.remove();
|
||||
if (tokens.isEmpty() && t != Tokens.END) {
|
||||
try {
|
||||
queueNextToken();
|
||||
} catch (ProblemException e) {
|
||||
tokens.add(e.problem());
|
||||
}
|
||||
if (tokens.isEmpty())
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"bug: tokens queue should not be empty here");
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import com.typesafe.config.ConfigException;
|
|||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/* FIXME the way the subclasses of Token are private with static isFoo and accessors is kind of ridiculous. */
|
||||
final class Tokens {
|
||||
static private class Value extends Token {
|
||||
|
||||
final private AbstractConfigValue value;
|
||||
|
||||
Value(AbstractConfigValue value) {
|
||||
super(TokenType.VALUE);
|
||||
super(TokenType.VALUE, value.origin());
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
|
@ -25,10 +26,7 @@ final class Tokens {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = tokenType().name() + "(" + value.valueType().name()
|
||||
+ ")";
|
||||
|
||||
return s + "='" + value().unwrapped() + "'";
|
||||
return "'" + value().unwrapped() + "' (" + value.valueType().name() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -48,20 +46,13 @@ final class Tokens {
|
|||
}
|
||||
|
||||
static private class Line extends Token {
|
||||
final private int lineNumber;
|
||||
|
||||
Line(int lineNumber) {
|
||||
super(TokenType.NEWLINE);
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
int lineNumber() {
|
||||
return lineNumber;
|
||||
Line(ConfigOrigin origin) {
|
||||
super(TokenType.NEWLINE, origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NEWLINE@" + lineNumber;
|
||||
return "'\n'@" + lineNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,38 +62,31 @@ final class Tokens {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other)
|
||||
&& ((Line) other).lineNumber == lineNumber;
|
||||
return super.equals(other) && ((Line) other).lineNumber() == lineNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 41 * (41 + super.hashCode()) + lineNumber;
|
||||
return 41 * (41 + super.hashCode()) + lineNumber();
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a Value, because it requires special processing
|
||||
static private class UnquotedText extends Token {
|
||||
final private ConfigOrigin origin;
|
||||
final private String value;
|
||||
|
||||
UnquotedText(ConfigOrigin origin, String s) {
|
||||
super(TokenType.UNQUOTED_TEXT);
|
||||
this.origin = origin;
|
||||
super(TokenType.UNQUOTED_TEXT, origin);
|
||||
this.value = s;
|
||||
}
|
||||
|
||||
ConfigOrigin origin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return tokenType().name() + "(" + value + ")";
|
||||
return "'" + value + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -122,23 +106,78 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
static private class Problem extends Token {
|
||||
final private String what;
|
||||
final private String message;
|
||||
final private boolean suggestQuotes;
|
||||
final private Throwable cause;
|
||||
|
||||
Problem(ConfigOrigin origin, String what, String message, boolean suggestQuotes,
|
||||
Throwable cause) {
|
||||
super(TokenType.PROBLEM, origin);
|
||||
this.what = what;
|
||||
this.message = message;
|
||||
this.suggestQuotes = suggestQuotes;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
boolean suggestQuotes() {
|
||||
return suggestQuotes;
|
||||
}
|
||||
|
||||
Throwable cause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('\'');
|
||||
sb.append(what);
|
||||
sb.append('\'');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof Problem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && ((Problem) other).what.equals(what)
|
||||
&& ((Problem) other).message.equals(message)
|
||||
&& ((Problem) other).suggestQuotes == suggestQuotes
|
||||
&& ConfigImplUtil.equalsHandlingNull(((Problem) other).cause, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 41 * (41 + super.hashCode());
|
||||
h = 41 * (h + what.hashCode());
|
||||
h = 41 * (h + message.hashCode());
|
||||
h = 41 * (h + Boolean.valueOf(suggestQuotes).hashCode());
|
||||
if (cause != null)
|
||||
h = 41 * (h + cause.hashCode());
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a Value, because it requires special processing
|
||||
static private class Substitution extends Token {
|
||||
final private ConfigOrigin origin;
|
||||
final private boolean optional;
|
||||
final private List<Token> value;
|
||||
|
||||
Substitution(ConfigOrigin origin, boolean optional, List<Token> expression) {
|
||||
super(TokenType.SUBSTITUTION);
|
||||
this.origin = origin;
|
||||
super(TokenType.SUBSTITUTION, origin);
|
||||
this.optional = optional;
|
||||
this.value = expression;
|
||||
}
|
||||
|
||||
ConfigOrigin origin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
boolean optional() {
|
||||
return optional;
|
||||
}
|
||||
|
|
@ -149,7 +188,11 @@ final class Tokens {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return tokenType().name() + "(" + value.toString() + ")";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Token t : value) {
|
||||
sb.append(t.toString());
|
||||
}
|
||||
return "'${" + sb.toString() + "}'";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -190,12 +233,32 @@ final class Tokens {
|
|||
return token instanceof Line;
|
||||
}
|
||||
|
||||
static int getLineNumber(Token token) {
|
||||
if (token instanceof Line) {
|
||||
return ((Line) token).lineNumber();
|
||||
static boolean isProblem(Token token) {
|
||||
return token instanceof Problem;
|
||||
}
|
||||
|
||||
static String getProblemMessage(Token token) {
|
||||
if (token instanceof Problem) {
|
||||
return ((Problem) token).message();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"tried to get line number from non-newline " + token);
|
||||
throw new ConfigException.BugOrBroken("tried to get problem message from " + token);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean getProblemSuggestQuotes(Token token) {
|
||||
if (token instanceof Problem) {
|
||||
return ((Problem) token).suggestQuotes();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken("tried to get problem suggestQuotes from "
|
||||
+ token);
|
||||
}
|
||||
}
|
||||
|
||||
static Throwable getProblemCause(Token token) {
|
||||
if (token instanceof Problem) {
|
||||
return ((Problem) token).cause();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken("tried to get problem cause from " + token);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,15 +275,6 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
static ConfigOrigin getUnquotedTextOrigin(Token token) {
|
||||
if (token instanceof UnquotedText) {
|
||||
return ((UnquotedText) token).origin();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"tried to get unquoted text from " + token);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isSubstitution(Token token) {
|
||||
return token instanceof Substitution;
|
||||
}
|
||||
|
|
@ -234,15 +288,6 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
static ConfigOrigin getSubstitutionOrigin(Token token) {
|
||||
if (token instanceof Substitution) {
|
||||
return ((Substitution) token).origin();
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"tried to get substitution origin from " + token);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean getSubstitutionOptional(Token token) {
|
||||
if (token instanceof Substitution) {
|
||||
return ((Substitution) token).optional();
|
||||
|
|
@ -252,18 +297,23 @@ final class Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
final static Token START = new Token(TokenType.START);
|
||||
final static Token END = new Token(TokenType.END);
|
||||
final static Token COMMA = new Token(TokenType.COMMA);
|
||||
final static Token EQUALS = new Token(TokenType.EQUALS);
|
||||
final static Token COLON = new Token(TokenType.COLON);
|
||||
final static Token OPEN_CURLY = new Token(TokenType.OPEN_CURLY);
|
||||
final static Token CLOSE_CURLY = new Token(TokenType.CLOSE_CURLY);
|
||||
final static Token OPEN_SQUARE = new Token(TokenType.OPEN_SQUARE);
|
||||
final static Token CLOSE_SQUARE = new Token(TokenType.CLOSE_SQUARE);
|
||||
final static Token START = Token.newWithoutOrigin(TokenType.START, "start of file");
|
||||
final static Token END = Token.newWithoutOrigin(TokenType.END, "end of file");
|
||||
final static Token COMMA = Token.newWithoutOrigin(TokenType.COMMA, "','");
|
||||
final static Token EQUALS = Token.newWithoutOrigin(TokenType.EQUALS, "'='");
|
||||
final static Token COLON = Token.newWithoutOrigin(TokenType.COLON, "':'");
|
||||
final static Token OPEN_CURLY = Token.newWithoutOrigin(TokenType.OPEN_CURLY, "'{'");
|
||||
final static Token CLOSE_CURLY = Token.newWithoutOrigin(TokenType.CLOSE_CURLY, "'}'");
|
||||
final static Token OPEN_SQUARE = Token.newWithoutOrigin(TokenType.OPEN_SQUARE, "'['");
|
||||
final static Token CLOSE_SQUARE = Token.newWithoutOrigin(TokenType.CLOSE_SQUARE, "']'");
|
||||
|
||||
static Token newLine(int lineNumberJustEnded) {
|
||||
return new Line(lineNumberJustEnded);
|
||||
static Token newLine(ConfigOrigin origin) {
|
||||
return new Line(origin);
|
||||
}
|
||||
|
||||
static Token newProblem(ConfigOrigin origin, String what, String message,
|
||||
boolean suggestQuotes, Throwable cause) {
|
||||
return new Problem(origin, what, message, suggestQuotes, cause);
|
||||
}
|
||||
|
||||
static Token newUnquotedText(ConfigOrigin origin, String s) {
|
||||
|
|
|
|||
|
|
@ -482,10 +482,10 @@ public class HashedWheelTimer implements Timer {
|
|||
buf.append("deadline: ");
|
||||
if (remaining > 0) {
|
||||
buf.append(remaining);
|
||||
buf.append(" ms later, ");
|
||||
buf.append(" ns later, ");
|
||||
} else if (remaining < 0) {
|
||||
buf.append(-remaining);
|
||||
buf.append(" ms ago, ");
|
||||
buf.append(" ns ago, ");
|
||||
} else {
|
||||
buf.append("now, ");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ akka {
|
|||
type = "Dispatcher" # 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" # Optional, will be a generated UUID if omitted
|
||||
name = "DefaultDispatcher" # Name used in log messages and thread names.
|
||||
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)
|
||||
|
|
@ -91,7 +91,8 @@ akka {
|
|||
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 for Dispatcher, set to 1 for complete fairness
|
||||
throughput = 5 # 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)
|
||||
# If positive then a bounded mailbox is used and the capacity is set using the property
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import java.net.{ InetAddress, UnknownHostException }
|
|||
* <li>toString that includes exception name, message and uuid</li>
|
||||
* <li>toLongString which also includes the stack trace</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class AkkaException(message: String = "", cause: Throwable = null) extends RuntimeException(message, cause) with Serializable {
|
||||
val uuid = "%s_%s".format(AkkaException.hostname, newUuid)
|
||||
|
|
|
|||
|
|
@ -28,29 +28,8 @@ trait AutoReceivedMessage extends Serializable
|
|||
|
||||
trait PossiblyHarmful
|
||||
|
||||
case class HotSwap(code: ActorContext ⇒ Actor.Receive, discardOld: Boolean = true) extends AutoReceivedMessage {
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*/
|
||||
def this(code: akka.japi.Function[ActorContext, Procedure[Any]], discardOld: Boolean) = {
|
||||
this((context: ActorContext) ⇒ {
|
||||
val behavior = code(context)
|
||||
val result: Actor.Receive = { case msg ⇒ behavior(msg) }
|
||||
result
|
||||
}, discardOld)
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API with default non-stacking behavior
|
||||
*/
|
||||
def this(code: akka.japi.Function[ActorContext, Procedure[Any]]) = this(code, true)
|
||||
}
|
||||
|
||||
case class Failed(cause: Throwable) extends AutoReceivedMessage with PossiblyHarmful
|
||||
|
||||
case object RevertHotSwap extends AutoReceivedMessage with PossiblyHarmful
|
||||
|
||||
case object PoisonPill extends AutoReceivedMessage with PossiblyHarmful
|
||||
|
||||
case object Kill extends AutoReceivedMessage with PossiblyHarmful
|
||||
|
|
@ -112,7 +91,7 @@ case class ActorInterruptedException private[akka] (cause: Throwable)
|
|||
/**
|
||||
* This message is thrown by default when an Actors behavior doesn't match a message
|
||||
*/
|
||||
case class UnhandledMessageException(msg: Any, ref: ActorRef = null) extends Exception {
|
||||
case class UnhandledMessageException(msg: Any, ref: ActorRef = null) extends RuntimeException {
|
||||
|
||||
def this(msg: String) = this(msg, null)
|
||||
|
||||
|
|
@ -186,8 +165,6 @@ object Actor {
|
|||
*
|
||||
* <p/>
|
||||
* The Actor's own ActorRef is available in the 'self' member variable.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait Actor {
|
||||
|
||||
|
|
@ -204,18 +181,17 @@ trait Actor {
|
|||
* [[akka.actor.UntypedActorContext]], which is the Java API of the actor
|
||||
* context.
|
||||
*/
|
||||
@transient
|
||||
protected[akka] implicit val context: ActorContext = {
|
||||
val contextStack = ActorCell.contextStack.get
|
||||
|
||||
def noContextError =
|
||||
throw new ActorInitializationException(
|
||||
"\n\tYou cannot create an instance of " + getClass.getName + " explicitly using the constructor (new)." +
|
||||
"\n\tYou cannot create an instance of [" + getClass.getName + "] explicitly using the constructor (new)." +
|
||||
"\n\tYou have to use one of the factory methods to create a new actor. Either use:" +
|
||||
"\n\t\t'val actor = context.actorOf[MyActor]' (to create a supervised child actor from within an actor), or" +
|
||||
"\n\t\t'val actor = system.actorOf(new MyActor(..))' (to create a top level actor from the ActorSystem), or" +
|
||||
"\n\t\t'val actor = context.actorOf[MyActor]' (to create a supervised child actor from within an actor), or" +
|
||||
"\n\t\t'val actor = system.actorOf(new MyActor(..))' (to create a top level actor from the ActorSystem)")
|
||||
"\n\t\t'val actor = context.actorOf(Props[MyActor])' (to create a supervised child actor from within an actor), or" +
|
||||
"\n\t\t'val actor = system.actorOf(Props(new MyActor(..)))' (to create a top level actor from the ActorSystem), or" +
|
||||
"\n\t\t'val actor = context.actorOf(Props[MyActor])' (to create a supervised child actor from within an actor), or" +
|
||||
"\n\t\t'val actor = system.actorOf(Props(new MyActor(..)))' (to create a top level actor from the ActorSystem)")
|
||||
|
||||
if (contextStack.isEmpty) noContextError
|
||||
val c = contextStack.head
|
||||
|
|
@ -267,14 +243,17 @@ trait Actor {
|
|||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when an Actor is started by invoking 'actor'.
|
||||
* Is called when an Actor is started.
|
||||
* Actors are automatically started asynchronously when created.
|
||||
* Empty default implementation.
|
||||
*/
|
||||
def preStart() {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when 'actor.stop()' is invoked.
|
||||
* Is called asynchronously after 'actor.stop()' is invoked.
|
||||
* Empty default implementation.
|
||||
*/
|
||||
def postStop() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
|
|||
import akka.event.Logging.{ Debug, Warning, Error }
|
||||
import akka.util.{ Duration, Helpers }
|
||||
import akka.japi.Procedure
|
||||
import java.io.{ NotSerializableException, ObjectOutputStream }
|
||||
|
||||
/**
|
||||
* The actor context - the view of the actor cell from the actor.
|
||||
|
|
@ -27,16 +28,16 @@ import akka.japi.Procedure
|
|||
* context.actorOf(props)
|
||||
*
|
||||
* // Scala
|
||||
* context.actorOf[MyActor]("name")
|
||||
* context.actorOf[MyActor]
|
||||
* context.actorOf(new MyActor(...))
|
||||
* context.actorOf(Props[MyActor]("name")
|
||||
* context.actorOf(Props[MyActor]
|
||||
* context.actorOf(Props(new MyActor(...))
|
||||
*
|
||||
* // Java
|
||||
* context.actorOf(classOf[MyActor]);
|
||||
* context.actorOf(new Creator<MyActor>() {
|
||||
* context.actorOf(Props(new Creator<MyActor>() {
|
||||
* public MyActor create() { ... }
|
||||
* });
|
||||
* context.actorOf(new Creator<MyActor>() {
|
||||
* context.actorOf(Props(new Creator<MyActor>() {
|
||||
* public MyActor create() { ... }
|
||||
* }, "name");
|
||||
* }}}
|
||||
|
|
@ -63,7 +64,12 @@ trait ActorContext extends ActorRefFactory {
|
|||
* When specified, the receive function should be able to handle a 'ReceiveTimeout' message.
|
||||
* 1 millisecond is the minimum supported timeout.
|
||||
*/
|
||||
def receiveTimeout_=(timeout: Option[Duration]): Unit
|
||||
def setReceiveTimeout(timeout: Duration): Unit
|
||||
|
||||
/**
|
||||
* Resets the current receive timeout.
|
||||
*/
|
||||
def resetReceiveTimeout(): Unit
|
||||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
|
||||
|
|
@ -72,19 +78,29 @@ trait ActorContext extends ActorRefFactory {
|
|||
*/
|
||||
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit
|
||||
|
||||
def hotswap: Stack[PartialFunction[Any, Unit]]
|
||||
|
||||
/**
|
||||
* Reverts the Actor behavior to the previous one in the hotswap stack.
|
||||
*/
|
||||
def unbecome(): Unit
|
||||
|
||||
/**
|
||||
* Returns the current message envelope.
|
||||
*/
|
||||
def currentMessage: Envelope
|
||||
|
||||
def currentMessage_=(invocation: Envelope): Unit
|
||||
/**
|
||||
* Returns a stack with the hotswapped behaviors (as Scala PartialFunction).
|
||||
*/
|
||||
def hotswap: Stack[PartialFunction[Any, Unit]]
|
||||
|
||||
/**
|
||||
* Returns the sender 'ActorRef' of the current message.
|
||||
*/
|
||||
def sender: ActorRef
|
||||
|
||||
/**
|
||||
* Returns all supervised children.
|
||||
*/
|
||||
def children: Iterable[ActorRef]
|
||||
|
||||
/**
|
||||
|
|
@ -103,41 +119,35 @@ trait ActorContext extends ActorRefFactory {
|
|||
*/
|
||||
implicit def system: ActorSystem
|
||||
|
||||
/**
|
||||
* Returns the supervising parent ActorRef.
|
||||
*/
|
||||
def parent: ActorRef
|
||||
|
||||
/**
|
||||
* Registers this actor as a Monitor for the provided ActorRef
|
||||
* Registers this actor as a Monitor for the provided ActorRef.
|
||||
* @return the provided ActorRef
|
||||
*/
|
||||
def watch(subject: ActorRef): ActorRef
|
||||
|
||||
/**
|
||||
* Unregisters this actor as Monitor for the provided ActorRef
|
||||
* Unregisters this actor as Monitor for the provided ActorRef.
|
||||
* @return the provided ActorRef
|
||||
*/
|
||||
def unwatch(subject: ActorRef): ActorRef
|
||||
|
||||
final protected def writeObject(o: ObjectOutputStream): Unit =
|
||||
throw new NotSerializableException("ActorContext is not serializable!")
|
||||
}
|
||||
|
||||
trait UntypedActorContext extends ActorContext {
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable Java Collection containing the linked actors,
|
||||
* please note that the backing map is thread-safe but not immutable
|
||||
*/
|
||||
def getChildren(): java.lang.Iterable[ActorRef]
|
||||
|
||||
/**
|
||||
* Gets the current receive timeout
|
||||
* When specified, the receive method should be able to handle a 'ReceiveTimeout' message.
|
||||
*/
|
||||
def getReceiveTimeout: Option[Duration]
|
||||
|
||||
/**
|
||||
* Defines the default timeout for an initial receive invocation.
|
||||
* When specified, the receive function should be able to handle a 'ReceiveTimeout' message.
|
||||
* 1 millisecond is the minimum supported timeout.
|
||||
*/
|
||||
def setReceiveTimeout(timeout: Duration): Unit
|
||||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Procedure' handler.
|
||||
* Puts the behavior on top of the hotswap stack.
|
||||
|
|
@ -191,7 +201,9 @@ private[akka] class ActorCell(
|
|||
|
||||
override final def receiveTimeout: Option[Duration] = if (receiveTimeoutData._1 > 0) Some(Duration(receiveTimeoutData._1, MILLISECONDS)) else None
|
||||
|
||||
override final def receiveTimeout_=(timeout: Option[Duration]): Unit = {
|
||||
override final def setReceiveTimeout(timeout: Duration): Unit = setReceiveTimeout(Some(timeout))
|
||||
|
||||
final def setReceiveTimeout(timeout: Option[Duration]): Unit = {
|
||||
val timeoutMs = timeout match {
|
||||
case None ⇒ -1L
|
||||
case Some(duration) ⇒
|
||||
|
|
@ -204,22 +216,14 @@ private[akka] class ActorCell(
|
|||
receiveTimeoutData = (timeoutMs, receiveTimeoutData._2)
|
||||
}
|
||||
|
||||
final override def resetReceiveTimeout(): Unit = setReceiveTimeout(None)
|
||||
|
||||
/**
|
||||
* In milliseconds
|
||||
*/
|
||||
final var receiveTimeoutData: (Long, Cancellable) =
|
||||
if (_receiveTimeout.isDefined) (_receiveTimeout.get.toMillis, emptyCancellable) else emptyReceiveTimeoutData
|
||||
|
||||
/**
|
||||
* UntypedActorContext impl
|
||||
*/
|
||||
final def getReceiveTimeout: Option[Duration] = receiveTimeout
|
||||
|
||||
/**
|
||||
* UntypedActorContext impl
|
||||
*/
|
||||
final def setReceiveTimeout(timeout: Duration): Unit = receiveTimeout = Some(timeout)
|
||||
|
||||
final var childrenRefs: TreeMap[String, ChildRestartStats] = emptyChildrenRefs
|
||||
|
||||
private def _actorOf(props: Props, name: String): ActorRef = {
|
||||
|
|
@ -392,7 +396,7 @@ private[akka] class ActorCell(
|
|||
def resume(): Unit = dispatcher resume this
|
||||
|
||||
def terminate() {
|
||||
receiveTimeout = None
|
||||
setReceiveTimeout(None)
|
||||
cancelReceiveTimeout
|
||||
|
||||
val c = children
|
||||
|
|
@ -515,8 +519,6 @@ private[akka] class ActorCell(
|
|||
if (system.settings.DebugAutoReceive) system.eventStream.publish(Debug(self.path.toString, "received AutoReceiveMessage " + msg))
|
||||
|
||||
msg.message match {
|
||||
case HotSwap(code, discardOld) ⇒ become(code(this), discardOld)
|
||||
case RevertHotSwap ⇒ unbecome()
|
||||
case Failed(cause) ⇒ handleFailure(sender, cause)
|
||||
case Kill ⇒ throw new ActorKilledException("Kill")
|
||||
case PoisonPill ⇒ self.stop()
|
||||
|
|
@ -527,11 +529,13 @@ private[akka] class ActorCell(
|
|||
}
|
||||
|
||||
private def doTerminate() {
|
||||
dispatcher.detach(this)
|
||||
|
||||
try {
|
||||
try {
|
||||
val a = actor
|
||||
if (a ne null) a.postStop()
|
||||
} finally {
|
||||
dispatcher.detach(this)
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
parent.sendSystemMessage(ChildTerminated(self))
|
||||
|
|
|
|||
|
|
@ -25,28 +25,26 @@ import akka.event.LoggingAdapter
|
|||
* <pre>
|
||||
* import Actor._
|
||||
*
|
||||
* val actor = actorOf[MyActor]
|
||||
* val actor = actorOf(Props[MyActor]
|
||||
* actor ! message
|
||||
* actor.stop()
|
||||
* </pre>
|
||||
*
|
||||
* You can also create and start actors like this:
|
||||
* <pre>
|
||||
* val actor = actorOf[MyActor]
|
||||
* val actor = actorOf(Props[MyActor]
|
||||
* </pre>
|
||||
*
|
||||
* Here is an example on how to create an actor with a non-default constructor.
|
||||
* <pre>
|
||||
* import Actor._
|
||||
*
|
||||
* val actor = actorOf(new MyActor(...))
|
||||
* val actor = actorOf(Props(new MyActor(...))
|
||||
* actor ! message
|
||||
* actor.stop()
|
||||
* </pre>
|
||||
*
|
||||
* The natural ordering of ActorRef is defined in terms of its [[akka.actor.ActorPath]].
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable {
|
||||
scalaRef: InternalActorRef ⇒
|
||||
|
|
@ -81,17 +79,29 @@ abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable
|
|||
final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* Akka Java API.
|
||||
*
|
||||
* Sends a message asynchronously returns a future holding the eventual reply message.
|
||||
* <p/>
|
||||
*
|
||||
* <b>NOTE:</b>
|
||||
* Use this method with care. In most cases it is better to use 'tell' together with the 'getContext().getSender()' to
|
||||
* implement request/response message exchanges.
|
||||
* <p/>
|
||||
* If you are sending messages using <code>ask</code> then you <b>have to</b> use <code>getContext().sender().tell(...)</code>
|
||||
* to send a reply message to the original sender. If not then the sender will block until the timeout expires.
|
||||
* Use this method with care. In most cases it is better to use 'tell' together with the sender
|
||||
* parameter to implement non-blocking request/response message exchanges.
|
||||
*
|
||||
* If you are sending messages using <code>ask</code> and using blocking operations on the Future, such as
|
||||
* 'get', then you <b>have to</b> use <code>getContext().sender().tell(...)</code>
|
||||
* in the target actor to send a reply message to the original sender, and thereby completing the Future,
|
||||
* otherwise the sender will block until the timeout expires.
|
||||
*
|
||||
* When using future callbacks, inside actors you need to carefully avoid closing over
|
||||
* the containing actor’s reference, i.e. do not call methods or access mutable state
|
||||
* on the enclosing actor from within the callback. This would break the actor
|
||||
* encapsulation and may introduce synchronization bugs and race conditions because
|
||||
* the callback will be scheduled concurrently to the enclosing actor. Unfortunately
|
||||
* there is not yet a way to detect these illegal accesses at compile time.
|
||||
*/
|
||||
def ask(message: AnyRef, timeout: Long): Future[AnyRef] = ?(message, Timeout(timeout)).asInstanceOf[Future[AnyRef]]
|
||||
def ask(message: AnyRef, timeout: Timeout): Future[AnyRef] = ?(message, timeout).asInstanceOf[Future[AnyRef]]
|
||||
|
||||
def ask(message: AnyRef, timeoutMillis: Long): Future[AnyRef] = ask(message, new Timeout(timeoutMillis))
|
||||
|
||||
/**
|
||||
* Forwards the message and passes the original sender actor as the sender.
|
||||
|
|
@ -146,6 +156,21 @@ trait ScalaActorRef { ref: ActorRef ⇒
|
|||
|
||||
/**
|
||||
* Sends a message asynchronously, returning a future which may eventually hold the reply.
|
||||
* <b>NOTE:</b>
|
||||
* Use this method with care. In most cases it is better to use '!' together with implicit or explicit
|
||||
* sender parameter to implement non-blocking request/response message exchanges.
|
||||
*
|
||||
* If you are sending messages using <code>ask</code> and using blocking operations on the Future, such as
|
||||
* 'get', then you <b>have to</b> use <code>getContext().sender().tell(...)</code>
|
||||
* in the target actor to send a reply message to the original sender, and thereby completing the Future,
|
||||
* otherwise the sender will block until the timeout expires.
|
||||
*
|
||||
* When using future callbacks, inside actors you need to carefully avoid closing over
|
||||
* the containing actor’s reference, i.e. do not call methods or access mutable state
|
||||
* on the enclosing actor from within the callback. This would break the actor
|
||||
* encapsulation and may introduce synchronization bugs and race conditions because
|
||||
* the callback will be scheduled concurrently to the enclosing actor. Unfortunately
|
||||
* there is not yet a way to detect these illegal accesses at compile time.
|
||||
*/
|
||||
def ?(message: Any)(implicit timeout: Timeout): Future[Any]
|
||||
|
||||
|
|
@ -186,8 +211,6 @@ private[akka] case object Nobody extends MinimalActorRef {
|
|||
|
||||
/**
|
||||
* Local (serializable) ActorRef that is used when referencing the Actor on its "home" node.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] class LocalActorRef private[akka] (
|
||||
_system: ActorSystemImpl,
|
||||
|
|
@ -473,7 +496,7 @@ class AskActorRef(
|
|||
}
|
||||
|
||||
override def ?(message: Any)(implicit timeout: Timeout): Future[Any] =
|
||||
new KeptPromise[Any](Left(new UnsupportedOperationException("Ask/? is not supported for %s".format(getClass.getName))))(dispatcher)
|
||||
new KeptPromise[Any](Left(new UnsupportedOperationException("Ask/? is not supported for [%s]".format(getClass.getName))))(dispatcher)
|
||||
|
||||
override def isTerminated = result.isCompleted || result.isExpired
|
||||
|
||||
|
|
|
|||
|
|
@ -154,82 +154,6 @@ trait ActorRefFactory {
|
|||
*/
|
||||
def actorOf(props: Props, name: String): ActorRef
|
||||
|
||||
/**
|
||||
* Create new actor of the given type as child of this context and give it an automatically
|
||||
* generated name (currently similar to base64-encoded integer count,
|
||||
* reversed and with “$” prepended, may change in the future). The type must have
|
||||
* a no-arg constructor which will be invoked using reflection.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf[T <: Actor](implicit m: Manifest[T]): ActorRef = actorOf(Props(m.erasure.asInstanceOf[Class[_ <: Actor]]))
|
||||
|
||||
/**
|
||||
* Create new actor of the given type as child of this context with the given name, which must
|
||||
* not be null, empty or start with “$”. If the given name is already in use,
|
||||
* and `InvalidActorNameException` is thrown. The type must have
|
||||
* a no-arg constructor which will be invoked using reflection.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf[T <: Actor](name: String)(implicit m: Manifest[T]): ActorRef =
|
||||
actorOf(Props(m.erasure.asInstanceOf[Class[_ <: Actor]]), name)
|
||||
|
||||
/**
|
||||
* Create new actor of the given class as child of this context and give it an automatically
|
||||
* generated name (currently similar to base64-encoded integer count,
|
||||
* reversed and with “$” prepended, may change in the future). The class must have
|
||||
* a no-arg constructor which will be invoked using reflection.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf[T <: Actor](clazz: Class[T]): ActorRef = actorOf(Props(clazz))
|
||||
|
||||
/**
|
||||
* Create new actor as child of this context and give it an automatically
|
||||
* generated name (currently similar to base64-encoded integer count,
|
||||
* reversed and with “$” prepended, may change in the future). Use this
|
||||
* method to pass constructor arguments to the [[akka.actor.Actor]] while using
|
||||
* only default [[akka.actor.Props]]; otherwise refer to `actorOf(Props)`.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf(factory: ⇒ Actor): ActorRef = actorOf(Props(() ⇒ factory))
|
||||
|
||||
/**
|
||||
* ''Java API'': Create new actor as child of this context and give it an
|
||||
* automatically generated name (currently similar to base64-encoded integer
|
||||
* count, reversed and with “$” prepended, may change in the future).
|
||||
*
|
||||
* Identical to `actorOf(Props(() => creator.create()))`.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf(creator: UntypedActorFactory): ActorRef = actorOf(Props(() ⇒ creator.create()))
|
||||
|
||||
/**
|
||||
* ''Java API'': Create new actor as child of this context with the given name, which must
|
||||
* not be null, empty or start with “$”. If the given name is already in use,
|
||||
* and `InvalidActorNameException` is thrown.
|
||||
*
|
||||
* Identical to `actorOf(Props(() => creator.create()), name)`.
|
||||
*
|
||||
* When invoked on ActorSystem, this method sends a message to the guardian
|
||||
* actor and blocks waiting for a reply, see `akka.actor.creation-timeout` in
|
||||
* the `reference.conf`.
|
||||
*/
|
||||
def actorOf(creator: UntypedActorFactory, name: String): ActorRef = actorOf(Props(() ⇒ creator.create()), name)
|
||||
|
||||
/**
|
||||
* Look-up an actor by path; if it does not exist, returns a reference to
|
||||
* the dead-letter mailbox of the [[akka.actor.ActorSystem]]. If the path
|
||||
|
|
@ -386,14 +310,14 @@ class LocalActorRefProvider(
|
|||
|
||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match {
|
||||
case Failed(ex) if sender ne null ⇒ causeOfTermination = Some(ex); sender.stop()
|
||||
case _ ⇒ log.error(this + " received unexpected message " + message)
|
||||
case _ ⇒ log.error(this + " received unexpected message [" + message + "]")
|
||||
})
|
||||
|
||||
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
|
||||
message match {
|
||||
case Supervise(child) ⇒ // TODO register child in some map to keep track of it and enable shutdown after all dead
|
||||
case ChildTerminated(child) ⇒ stop()
|
||||
case _ ⇒ log.error(this + " received unexpected system message " + message)
|
||||
case _ ⇒ log.error(this + " received unexpected system message [" + message + "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,8 +104,7 @@ object ActorSystem {
|
|||
val SchedulerTicksPerWheel = getInt("akka.scheduler.ticksPerWheel")
|
||||
|
||||
if (ConfigVersion != Version)
|
||||
throw new ConfigurationException("Akka JAR version [" + Version +
|
||||
"] does not match the provided config version [" + ConfigVersion + "]")
|
||||
throw new ConfigurationException("Akka JAR version [" + Version + "] does not match the provided config version [" + ConfigVersion + "]")
|
||||
|
||||
override def toString: String = config.root.render
|
||||
}
|
||||
|
|
@ -168,16 +167,16 @@ object ActorSystem {
|
|||
* system.actorOf(props)
|
||||
*
|
||||
* // Scala
|
||||
* system.actorOf[MyActor]("name")
|
||||
* system.actorOf[MyActor]
|
||||
* system.actorOf(new MyActor(...))
|
||||
* system.actorOf(Props[MyActor]("name")
|
||||
* system.actorOf(Props[MyActor]
|
||||
* system.actorOf(Props(new MyActor(...))
|
||||
*
|
||||
* // Java
|
||||
* system.actorOf(classOf[MyActor]);
|
||||
* system.actorOf(new Creator<MyActor>() {
|
||||
* system.actorOf(Props(new Creator<MyActor>() {
|
||||
* public MyActor create() { ... }
|
||||
* });
|
||||
* system.actorOf(new Creator<MyActor>() {
|
||||
* system.actorOf(Props(new Creator<MyActor>() {
|
||||
* public MyActor create() { ... }
|
||||
* }, "name");
|
||||
* }}}
|
||||
|
|
@ -262,12 +261,13 @@ abstract class ActorSystem extends ActorRefFactory {
|
|||
* effort basis and hence not strictly guaranteed.
|
||||
*/
|
||||
def deadLetters: ActorRef
|
||||
|
||||
//#scheduler
|
||||
/**
|
||||
* Light-weight scheduler for running asynchronous tasks after some deadline
|
||||
* in the future. Not terribly precise but cheap.
|
||||
*/
|
||||
def scheduler: Scheduler
|
||||
//#scheduler
|
||||
|
||||
/**
|
||||
* Helper object for creating new dispatchers and passing in all required
|
||||
|
|
@ -327,7 +327,7 @@ abstract class ActorSystem extends ActorRefFactory {
|
|||
class ActorSystemImpl(val name: String, applicationConfig: Config) extends ActorSystem {
|
||||
|
||||
if (!name.matches("""^\w+$"""))
|
||||
throw new IllegalArgumentException("invalid ActorSystem name '" + name + "', must contain only word characters (i.e. [a-zA-Z_0-9])")
|
||||
throw new IllegalArgumentException("invalid ActorSystem name [" + name + "], must contain only word characters (i.e. [a-zA-Z_0-9])")
|
||||
|
||||
import ActorSystem._
|
||||
|
||||
|
|
@ -492,7 +492,7 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
|
|||
extensions.putIfAbsent(ext, inProcessOfRegistration) match { // Signal that registration is in process
|
||||
case null ⇒ try { // Signal was successfully sent
|
||||
ext.createExtension(this) match { // Create and initialize the extension
|
||||
case null ⇒ throw new IllegalStateException("Extension instance created as null for Extension: " + ext)
|
||||
case null ⇒ throw new IllegalStateException("Extension instance created as 'null' for extension [" + ext + "]")
|
||||
case instance ⇒
|
||||
extensions.replace(ext, inProcessOfRegistration, instance) //Replace our in process signal with the initialized extension
|
||||
instance //Profit!
|
||||
|
|
@ -511,7 +511,7 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
|
|||
}
|
||||
|
||||
def extension[T <: Extension](ext: ExtensionId[T]): T = findExtension(ext) match {
|
||||
case null ⇒ throw new IllegalArgumentException("Trying to get non-registered extension " + ext)
|
||||
case null ⇒ throw new IllegalArgumentException("Trying to get non-registered extension [" + ext + "]")
|
||||
case some ⇒ some.asInstanceOf[T]
|
||||
}
|
||||
|
||||
|
|
@ -524,8 +524,8 @@ class ActorSystemImpl(val name: String, applicationConfig: Config) extends Actor
|
|||
getObjectFor[AnyRef](fqcn).fold(_ ⇒ createInstance[AnyRef](fqcn, noParams, noArgs), Right(_)) match {
|
||||
case Right(p: ExtensionIdProvider) ⇒ registerExtension(p.lookup());
|
||||
case Right(p: ExtensionId[_]) ⇒ registerExtension(p);
|
||||
case Right(other) ⇒ log.error("'{}' is not an ExtensionIdProvider or ExtensionId, skipping...", fqcn)
|
||||
case Left(problem) ⇒ log.error(problem, "While trying to load extension '{}', skipping...", fqcn)
|
||||
case Right(other) ⇒ log.error("[{}] is not an 'ExtensionIdProvider' or 'ExtensionId', skipping...", fqcn)
|
||||
case Left(problem) ⇒ log.error(problem, "While trying to load extension [{}], skipping...", fqcn)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -564,7 +564,6 @@ trait FSM[S, D] extends ListenerManagement {
|
|||
/**
|
||||
* Stackable trait for FSM which adds a rolling event log.
|
||||
*
|
||||
* @author Roland Kuhn
|
||||
* @since 1.2
|
||||
*/
|
||||
trait LoggingFSM[S, D] extends FSM[S, D] { this: Actor ⇒
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ trait IO {
|
|||
private def run() {
|
||||
_next match {
|
||||
case ByteStringLength(continuation, handle, message, waitingFor) ⇒
|
||||
context.currentMessage = message
|
||||
context.asInstanceOf[ActorCell].currentMessage = message
|
||||
val st = state(handle)
|
||||
if (st.readBytes.length >= waitingFor) {
|
||||
val bytes = st.readBytes.take(waitingFor) //.compact
|
||||
|
|
@ -202,7 +202,7 @@ trait IO {
|
|||
run()
|
||||
}
|
||||
case bsd @ ByteStringDelimited(continuation, handle, message, delimiter, inclusive, scanned) ⇒
|
||||
context.currentMessage = message
|
||||
context.asInstanceOf[ActorCell].currentMessage = message
|
||||
val st = state(handle)
|
||||
val idx = st.readBytes.indexOfSlice(delimiter, scanned)
|
||||
if (idx >= 0) {
|
||||
|
|
@ -215,7 +215,7 @@ trait IO {
|
|||
_next = bsd.copy(scanned = math.min(idx - delimiter.length, 0))
|
||||
}
|
||||
case ByteStringAny(continuation, handle, message) ⇒
|
||||
context.currentMessage = message
|
||||
context.asInstanceOf[ActorCell].currentMessage = message
|
||||
val st = state(handle)
|
||||
if (st.readBytes.length > 0) {
|
||||
val bytes = st.readBytes //.compact
|
||||
|
|
|
|||
|
|
@ -86,6 +86,25 @@ case class Props(creator: () ⇒ Actor = Props.defaultCreator,
|
|||
timeout: Timeout = Props.defaultTimeout,
|
||||
faultHandler: FaultHandlingStrategy = Props.defaultFaultHandler,
|
||||
routerConfig: RouterConfig = Props.defaultRoutedProps) {
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*/
|
||||
def this(factory: UntypedActorFactory) = this(
|
||||
creator = () ⇒ factory.create(),
|
||||
dispatcher = Props.defaultDispatcher,
|
||||
timeout = Props.defaultTimeout,
|
||||
faultHandler = Props.defaultFaultHandler)
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*/
|
||||
def this(actorClass: Class[_ <: Actor]) = this(
|
||||
creator = () ⇒ actorClass.newInstance,
|
||||
dispatcher = Props.defaultDispatcher,
|
||||
timeout = Props.defaultTimeout,
|
||||
faultHandler = Props.defaultFaultHandler)
|
||||
|
||||
/**
|
||||
* No-args constructor that sets all the default values
|
||||
* Java API
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
package akka.actor
|
||||
|
||||
import akka.util.Duration
|
||||
|
||||
//#scheduler
|
||||
/**
|
||||
* An Akka scheduler service. This one needs one special behavior: if
|
||||
* Closeable, it MUST execute all outstanding tasks upon .close() in order
|
||||
|
|
@ -28,6 +28,8 @@ trait Scheduler {
|
|||
* Schedules a message to be sent repeatedly with an initial delay and frequency.
|
||||
* E.g. if you would like a message to be sent immediately and thereafter every 500ms you would set
|
||||
* delay = Duration.Zero and frequency = Duration(500, TimeUnit.MILLISECONDS)
|
||||
*
|
||||
* Java & Scala API
|
||||
*/
|
||||
def schedule(initialDelay: Duration, frequency: Duration, receiver: ActorRef, message: Any): Cancellable
|
||||
|
||||
|
|
@ -35,33 +37,53 @@ trait Scheduler {
|
|||
* Schedules a function to be run repeatedly with an initial delay and a frequency.
|
||||
* E.g. if you would like the function to be run after 2 seconds and thereafter every 100ms you would set
|
||||
* delay = Duration(2, TimeUnit.SECONDS) and frequency = Duration(100, TimeUnit.MILLISECONDS)
|
||||
*
|
||||
* Scala API
|
||||
*/
|
||||
def schedule(initialDelay: Duration, frequency: Duration)(f: ⇒ Unit): Cancellable
|
||||
|
||||
/**
|
||||
* Schedules a Runnable to be run once with a delay, i.e. a time period that has to pass before the runnable is executed.
|
||||
*
|
||||
* Java & Scala API
|
||||
*/
|
||||
def scheduleOnce(delay: Duration, runnable: Runnable): Cancellable
|
||||
|
||||
/**
|
||||
* Schedules a message to be sent once with a delay, i.e. a time period that has to pass before the message is sent.
|
||||
*
|
||||
* Java & Scala API
|
||||
*/
|
||||
def scheduleOnce(delay: Duration, receiver: ActorRef, message: Any): Cancellable
|
||||
|
||||
/**
|
||||
* Schedules a function to be run once with a delay, i.e. a time period that has to pass before the function is run.
|
||||
*
|
||||
* Scala API
|
||||
*/
|
||||
def scheduleOnce(delay: Duration)(f: ⇒ Unit): Cancellable
|
||||
}
|
||||
//#scheduler
|
||||
|
||||
//#cancellable
|
||||
/**
|
||||
* Signifies something that can be cancelled
|
||||
* There is no strict guarantee that the implementation is thread-safe,
|
||||
* but it should be good practice to make it so.
|
||||
*/
|
||||
trait Cancellable {
|
||||
/**
|
||||
* Cancels the underlying scheduled task.
|
||||
* Cancels this Cancellable
|
||||
*
|
||||
* Java & Scala API
|
||||
*/
|
||||
def cancel(): Unit
|
||||
|
||||
/**
|
||||
* Checks if the underlying scheduled task has been cancelled.
|
||||
* Returns whether this Cancellable has been cancelled
|
||||
*
|
||||
* Java & Scala API
|
||||
*/
|
||||
def isCancelled: Boolean
|
||||
}
|
||||
//#cancellable
|
||||
|
|
@ -48,8 +48,6 @@ import akka.dispatch.{ MessageDispatcher, Promise }
|
|||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class UntypedActor extends Actor {
|
||||
|
||||
|
|
@ -75,30 +73,36 @@ abstract class UntypedActor extends Actor {
|
|||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when an Actor is started, this only happens at most once in the life of an actor.
|
||||
* Is called when an Actor is started.
|
||||
* Actor are automatically started asynchronously when created.
|
||||
* Empty default implementation.
|
||||
*/
|
||||
override def preStart() {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when 'actor.stop()' is invoked.
|
||||
* Is called asynchronously after 'actor.stop()' is invoked.
|
||||
* Empty default implementation.
|
||||
*/
|
||||
override def postStop() {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called on a crashed Actor right BEFORE it is restarted to allow clean up of resources before Actor is terminated.
|
||||
* Is called on a crashed Actor right BEFORE it is restarted to allow clean
|
||||
* up of resources before Actor is terminated.
|
||||
* By default it calls postStop()
|
||||
*/
|
||||
override def preRestart(reason: Throwable, lastMessage: Option[Any]) {}
|
||||
override def preRestart(reason: Throwable, message: Option[Any]) { postStop() }
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash.
|
||||
* By default it calls preStart()
|
||||
*/
|
||||
override def postRestart(reason: Throwable) {}
|
||||
override def postRestart(reason: Throwable) { preStart() }
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
|
|
@ -117,7 +121,5 @@ abstract class UntypedActor extends Actor {
|
|||
|
||||
/**
|
||||
* Factory closure for an UntypedActor, to be used with 'Actors.actorOf(factory)'.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait UntypedActorFactory extends Creator[Actor]
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ import akka.event.EventStream
|
|||
import akka.actor.ActorSystem.Settings
|
||||
import com.typesafe.config.Config
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final case class Envelope(val message: Any, val sender: ActorRef) {
|
||||
if (message.isInstanceOf[AnyRef] && (message.asInstanceOf[AnyRef] eq null)) throw new InvalidMessageException("Message is null")
|
||||
}
|
||||
|
|
@ -87,9 +84,6 @@ object MessageDispatcher {
|
|||
implicit def defaultDispatcher(implicit system: ActorSystem) = system.dispatcher
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) extends AbstractMessageDispatcher with Serializable {
|
||||
|
||||
import MessageDispatcher._
|
||||
|
|
@ -138,7 +132,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
shutdownScheduleUpdater.get(this) match {
|
||||
case UNSCHEDULED ⇒
|
||||
if (shutdownScheduleUpdater.compareAndSet(this, UNSCHEDULED, SCHEDULED)) {
|
||||
scheduler.scheduleOnce(shutdownTimeout, shutdownAction)
|
||||
scheduleShutdownAction()
|
||||
()
|
||||
} else ifSensibleToDoSoThenScheduleShutdown()
|
||||
case SCHEDULED ⇒
|
||||
|
|
@ -149,6 +143,13 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
case _ ⇒ ()
|
||||
}
|
||||
|
||||
private def scheduleShutdownAction(): Unit = {
|
||||
// IllegalStateException is thrown if scheduler has been shutdown
|
||||
try scheduler.scheduleOnce(shutdownTimeout, shutdownAction) catch {
|
||||
case _: IllegalStateException ⇒ shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
private final val taskCleanup: () ⇒ Unit =
|
||||
() ⇒ if (inhabitantsUpdater.decrementAndGet(this) == 0) ifSensibleToDoSoThenScheduleShutdown()
|
||||
|
||||
|
|
@ -169,36 +170,9 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
val mailBox = actor.mailbox
|
||||
mailBox.becomeClosed() // FIXME reschedule in tell if possible race with cleanUp is detected in order to properly clean up
|
||||
actor.mailbox = deadLetterMailbox
|
||||
cleanUpMailboxFor(actor, mailBox)
|
||||
mailBox.cleanUp()
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable callback to clean up the mailbox for a given actor,
|
||||
* called when an actor is unregistered.
|
||||
*/
|
||||
protected def cleanUpMailboxFor(actor: ActorCell, mailBox: Mailbox) {
|
||||
|
||||
if (mailBox.hasSystemMessages) {
|
||||
var message = mailBox.systemDrain()
|
||||
while (message ne null) {
|
||||
// message must be “virgin” before being able to systemEnqueue again
|
||||
val next = message.next
|
||||
message.next = null
|
||||
deadLetterMailbox.systemEnqueue(actor.self, message)
|
||||
message = next
|
||||
}
|
||||
}
|
||||
|
||||
if (mailBox.hasMessages) {
|
||||
var envelope = mailBox.dequeue
|
||||
while (envelope ne null) {
|
||||
deadLetterMailbox.enqueue(actor.self, envelope)
|
||||
envelope = mailBox.dequeue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val shutdownAction = new Runnable {
|
||||
@tailrec
|
||||
final def run() {
|
||||
|
|
@ -213,9 +187,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
}
|
||||
case RESCHEDULED ⇒
|
||||
if (shutdownScheduleUpdater.compareAndSet(MessageDispatcher.this, RESCHEDULED, SCHEDULED))
|
||||
try scheduler.scheduleOnce(shutdownTimeout, this) catch {
|
||||
case _: IllegalStateException ⇒ shutdown()
|
||||
}
|
||||
scheduleShutdownAction()
|
||||
else run()
|
||||
}
|
||||
}
|
||||
|
|
@ -224,7 +196,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
/**
|
||||
* When the dispatcher no longer has any actors registered, how long will it wait until it shuts itself down,
|
||||
* defaulting to your akka configs "akka.actor.dispatcher-shutdown-timeout" or default specified in
|
||||
* akka-actor-reference.conf
|
||||
* reference.conf
|
||||
*/
|
||||
protected[akka] def shutdownTimeout: Duration
|
||||
|
||||
|
|
@ -290,7 +262,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
}
|
||||
|
||||
/**
|
||||
* Trait to be used for hooking in new dispatchers into Dispatchers.fromConfig
|
||||
* Trait to be used for hooking in new dispatchers into Dispatchers.from(cfg: Config)
|
||||
*/
|
||||
abstract class MessageDispatcherConfigurator() {
|
||||
/**
|
||||
|
|
@ -307,7 +279,8 @@ abstract class MessageDispatcherConfigurator() {
|
|||
}
|
||||
}
|
||||
|
||||
def configureThreadPool(config: Config,
|
||||
def configureThreadPool(
|
||||
config: Config,
|
||||
settings: Settings,
|
||||
createDispatcher: ⇒ (ThreadPoolConfig) ⇒ MessageDispatcher): ThreadPoolConfigDispatcherBuilder = {
|
||||
import ThreadPoolConfigDispatcherBuilder.conf_?
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ import akka.util.Duration
|
|||
*
|
||||
* @see akka.dispatch.BalancingDispatcher
|
||||
* @see akka.dispatch.Dispatchers
|
||||
*
|
||||
* @author Viktor Klang
|
||||
*/
|
||||
class BalancingDispatcher(
|
||||
_prerequisites: DispatcherPrerequisites,
|
||||
|
|
@ -65,6 +63,21 @@ class BalancingDispatcher(
|
|||
final def numberOfMessages: Int = messageQueue.numberOfMessages
|
||||
|
||||
final def hasMessages: Boolean = messageQueue.hasMessages
|
||||
|
||||
override def cleanUp(): Unit = {
|
||||
//Don't call the original implementation of this since it scraps all messages, and we don't want to do that
|
||||
if (hasSystemMessages) {
|
||||
val dlq = actor.systemImpl.deadLetterMailbox
|
||||
var message = systemDrain()
|
||||
while (message ne null) {
|
||||
// message must be “virgin” before being able to systemEnqueue again
|
||||
val next = message.next
|
||||
message.next = null
|
||||
dlq.systemEnqueue(actor.self, message)
|
||||
message = next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected[akka] override def register(actor: ActorCell) = {
|
||||
|
|
@ -78,19 +91,6 @@ class BalancingDispatcher(
|
|||
intoTheFray(except = actor) //When someone leaves, he tosses a friend into the fray
|
||||
}
|
||||
|
||||
protected override def cleanUpMailboxFor(actor: ActorCell, mailBox: Mailbox) {
|
||||
if (mailBox.hasSystemMessages) {
|
||||
var message = mailBox.systemDrain()
|
||||
while (message ne null) {
|
||||
// message must be “virgin” before being able to systemEnqueue again
|
||||
val next = message.next
|
||||
message.next = null
|
||||
prerequisites.deadLetterMailbox.systemEnqueue(actor.self, message)
|
||||
message = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def intoTheFray(except: ActorCell): Unit =
|
||||
if (rebalance.compareAndSet(false, true)) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ import java.util.concurrent._
|
|||
* But the preferred way of creating dispatchers is to use
|
||||
* the {@link akka.dispatch.Dispatchers} factory object.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
* @param throughput positive integer indicates the dispatcher will only process so much messages at a time from the
|
||||
* mailbox, without checking the mailboxes of other actors. Zero or negative means the dispatcher
|
||||
* always continues until the mailbox is empty.
|
||||
|
|
|
|||
|
|
@ -4,16 +4,19 @@
|
|||
|
||||
package akka.dispatch
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import akka.actor.LocalActorRef
|
||||
import akka.actor.newUuid
|
||||
import akka.util.{ Duration, ReflectiveAccess }
|
||||
import java.util.concurrent.TimeUnit
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.EventStream
|
||||
import akka.actor.Scheduler
|
||||
import akka.actor.ActorSystem.Settings
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import akka.config.ConfigurationException
|
||||
|
||||
trait DispatcherPrerequisites {
|
||||
def eventStream: EventStream
|
||||
|
|
@ -27,6 +30,10 @@ case class DefaultDispatcherPrerequisites(
|
|||
val scheduler: Scheduler) extends DispatcherPrerequisites
|
||||
|
||||
/**
|
||||
* It is recommended to define the dispatcher in configuration to allow for tuning
|
||||
* for different environments. Use the `lookup` or `newFromConfig` method to create
|
||||
* a dispatcher as specified in configuration.
|
||||
*
|
||||
* Scala API. Dispatcher factory.
|
||||
* <p/>
|
||||
* Example usage:
|
||||
|
|
@ -53,8 +60,6 @@ case class DefaultDispatcherPrerequisites(
|
|||
* .build();
|
||||
* </pre>
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: DispatcherPrerequisites) {
|
||||
|
||||
|
|
@ -64,9 +69,33 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
|
||||
val defaultDispatcherConfig = settings.config.getConfig("akka.actor.default-dispatcher")
|
||||
|
||||
// TODO PN Shouldn't we fail hard if default-dispatcher is wrong?
|
||||
lazy val defaultGlobalDispatcher =
|
||||
from(defaultDispatcherConfig) getOrElse newDispatcher("AkkaDefaultGlobalDispatcher", 1, MailboxType).build
|
||||
lazy val defaultGlobalDispatcher: MessageDispatcher =
|
||||
from(defaultDispatcherConfig) getOrElse {
|
||||
throw new ConfigurationException("Wrong configuration [akka.actor.default-dispatcher]")
|
||||
}
|
||||
|
||||
// FIXME: Dispatchers registered here are are not removed, see ticket #1494
|
||||
private val dispatchers = new ConcurrentHashMap[String, MessageDispatcher]
|
||||
|
||||
/**
|
||||
* Returns a dispatcher as specified in configuration, or if not defined it uses
|
||||
* the default dispatcher. The same dispatcher instance is returned for subsequent
|
||||
* lookups.
|
||||
*/
|
||||
def lookup(key: String): MessageDispatcher = {
|
||||
dispatchers.get(key) match {
|
||||
case null ⇒
|
||||
// It doesn't matter if we create a dispatcher that isn't used due to concurrent lookup.
|
||||
// That shouldn't happen often and in case it does the actual ExecutorService isn't
|
||||
// created until used, i.e. cheap.
|
||||
val newDispatcher = newFromConfig(key)
|
||||
dispatchers.putIfAbsent(key, newDispatcher) match {
|
||||
case null ⇒ newDispatcher
|
||||
case existing ⇒ existing
|
||||
}
|
||||
case existing ⇒ existing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an thread based dispatcher serving a single actor through the same single thread.
|
||||
|
|
@ -135,7 +164,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
new Dispatcher(prerequisites, name, throughput, throughputDeadline, mailboxType, config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
|
||||
|
||||
/**
|
||||
* Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool.
|
||||
* Creates a executor-based event-driven dispatcher, with work-sharing, serving multiple (millions) of actors through a thread pool.
|
||||
* <p/>
|
||||
* Has a fluent builder interface for configuring its semantics.
|
||||
*/
|
||||
|
|
@ -144,7 +173,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
settings.DispatcherThroughputDeadlineTime, MailboxType, config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
|
||||
|
||||
/**
|
||||
* Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool.
|
||||
* Creates a executor-based event-driven dispatcher, with work-sharing, serving multiple (millions) of actors through a thread pool.
|
||||
* <p/>
|
||||
* Has a fluent builder interface for configuring its semantics.
|
||||
*/
|
||||
|
|
@ -154,7 +183,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
|
||||
|
||||
/**
|
||||
* Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool.
|
||||
* Creates a executor-based event-driven dispatcher, with work-sharing, serving multiple (millions) of actors through a thread pool.
|
||||
* <p/>
|
||||
* Has a fluent builder interface for configuring its semantics.
|
||||
*/
|
||||
|
|
@ -164,7 +193,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
|
||||
|
||||
/**
|
||||
* Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool.
|
||||
* Creates a executor-based event-driven dispatcher, with work-sharing, serving multiple (millions) of actors through a thread pool.
|
||||
* <p/>
|
||||
* Has a fluent builder interface for configuring its semantics.
|
||||
*/
|
||||
|
|
@ -172,13 +201,13 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
ThreadPoolConfigDispatcherBuilder(config ⇒
|
||||
new BalancingDispatcher(prerequisites, name, throughput, throughputDeadline, mailboxType,
|
||||
config, settings.DispatcherDefaultShutdown), ThreadPoolConfig())
|
||||
|
||||
/**
|
||||
* Utility function that tries to load the specified dispatcher config from the akka.conf
|
||||
* Creates a new dispatcher as specified in configuration
|
||||
* or if not defined it uses the supplied dispatcher.
|
||||
* Uses default values from default-dispatcher, i.e. all options doesn't need to be defined
|
||||
* in config.
|
||||
* Uses default values from default-dispatcher, i.e. all options doesn't need to be defined.
|
||||
*/
|
||||
def fromConfig(key: String, default: ⇒ MessageDispatcher = defaultGlobalDispatcher, cfg: Config = settings.config): MessageDispatcher = {
|
||||
def newFromConfig(key: String, default: ⇒ MessageDispatcher, cfg: Config): MessageDispatcher = {
|
||||
import scala.collection.JavaConverters._
|
||||
def simpleName = key.substring(key.lastIndexOf('.') + 1)
|
||||
cfg.hasPath(key) match {
|
||||
|
|
@ -186,10 +215,18 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
case true ⇒
|
||||
val conf = cfg.getConfig(key)
|
||||
val confWithName = conf.withFallback(ConfigFactory.parseMap(Map("name" -> simpleName).asJava))
|
||||
from(confWithName).getOrElse(default)
|
||||
from(confWithName).getOrElse(throw new ConfigurationException("Wrong configuration [%s]".format(key)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dispatcher as specified in configuration, or if not defined it uses
|
||||
* the default dispatcher.
|
||||
* Uses default configuration values from default-dispatcher, i.e. all options doesn't
|
||||
* need to be defined.
|
||||
*/
|
||||
def newFromConfig(key: String): MessageDispatcher = newFromConfig(key, defaultGlobalDispatcher, settings.config)
|
||||
|
||||
/*
|
||||
* Creates of obtains a dispatcher from a ConfigMap according to the format below.
|
||||
* Uses default values from default-dispatcher.
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ object Mailbox {
|
|||
final val debug = false
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class Mailbox(val actor: ActorCell) extends MessageQueue with SystemMessageQueue with Runnable {
|
||||
import Mailbox._
|
||||
|
||||
|
|
@ -209,8 +206,29 @@ abstract class Mailbox(val actor: ActorCell) extends MessageQueue with SystemMes
|
|||
/**
|
||||
* Overridable callback to clean up the mailbox,
|
||||
* called when an actor is unregistered.
|
||||
* By default it dequeues all system messages + messages and ships them to the owning actors' systems' DeadLetterMailbox
|
||||
*/
|
||||
protected[dispatch] def cleanUp() {}
|
||||
protected[dispatch] def cleanUp(): Unit = if (actor ne null) {
|
||||
val dlq = actor.systemImpl.deadLetterMailbox
|
||||
if (hasSystemMessages) {
|
||||
var message = systemDrain()
|
||||
while (message ne null) {
|
||||
// message must be “virgin” before being able to systemEnqueue again
|
||||
val next = message.next
|
||||
message.next = null
|
||||
dlq.systemEnqueue(actor.self, message)
|
||||
message = next
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMessages) {
|
||||
var envelope = dequeue
|
||||
while (envelope ne null) {
|
||||
dlq.enqueue(actor.self, envelope)
|
||||
envelope = dequeue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait MessageQueue {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import java.util.concurrent.TimeUnit
|
|||
|
||||
/**
|
||||
* Dedicates a unique thread for each actor passed in as reference. Served through its messageQueue.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class PinnedDispatcher(
|
||||
_prerequisites: DispatcherPrerequisites,
|
||||
|
|
@ -32,7 +30,7 @@ class PinnedDispatcher(
|
|||
_shutdownTimeout) {
|
||||
|
||||
@volatile
|
||||
protected[akka] var owner: ActorCell = _actor
|
||||
private var owner: ActorCell = _actor
|
||||
|
||||
//Relies on an external lock provided by MessageDispatcher.attach
|
||||
protected[akka] override def register(actorCell: ActorCell) = {
|
||||
|
|
|
|||
|
|
@ -144,9 +144,6 @@ case class ThreadPoolConfigDispatcherBuilder(dispatcherFactory: (ThreadPoolConfi
|
|||
def configure(fs: Option[Function[ThreadPoolConfigDispatcherBuilder, ThreadPoolConfigDispatcherBuilder]]*): ThreadPoolConfigDispatcherBuilder = fs.foldLeft(this)((c, f) ⇒ f.map(_(c)).getOrElse(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThreadFactory(val name: String, val daemonic: Boolean = false) extends ThreadFactory {
|
||||
protected val counter = new AtomicLong
|
||||
|
||||
|
|
@ -157,9 +154,6 @@ class MonitorableThreadFactory(val name: String, val daemonic: Boolean = false)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object MonitorableThread {
|
||||
val DEFAULT_NAME = "MonitorableThread".intern
|
||||
|
||||
|
|
@ -168,9 +162,6 @@ object MonitorableThread {
|
|||
val alive = new AtomicInteger
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MonitorableThread(runnable: Runnable, name: String)
|
||||
extends Thread(runnable, name + "-" + MonitorableThread.created.incrementAndGet) {
|
||||
|
||||
|
|
|
|||
|
|
@ -281,19 +281,34 @@ object Logging {
|
|||
val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern
|
||||
|
||||
/**
|
||||
* Obtain LoggingAdapter for the given application and source object. The
|
||||
* source is used to identify the source of this logging channel and must have
|
||||
* Obtain LoggingAdapter for the given event stream (system) and source object.
|
||||
* Note that there is an implicit conversion from [[akka.actor.ActorSystem]]
|
||||
* to [[akka.event.LoggingBus]].
|
||||
*
|
||||
* The source is used to identify the source of this logging channel and must have
|
||||
* a corresponding LogSource[T] instance in scope; by default these are
|
||||
* provided for Class[_], Actor, ActorRef and String types.
|
||||
* provided for Class[_], Actor, ActorRef and String types. The source
|
||||
* object is translated to a String according to the following rules:
|
||||
* <ul>
|
||||
* <li>if it is an Actor or ActorRef, its path is used</li>
|
||||
* <li>in case of a String it is used as is</li>
|
||||
* <li>in case of a class an approximation of its simpleName
|
||||
* <li>and in all other cases the simpleName of its class</li>
|
||||
* </ul>
|
||||
*/
|
||||
def apply[T: LogSource](eventStream: LoggingBus, logSource: T): LoggingAdapter =
|
||||
new BusLogging(eventStream, implicitly[LogSource[T]].genString(logSource))
|
||||
|
||||
/**
|
||||
* Java API: Obtain LoggingAdapter for the given application and source object. The
|
||||
* source object is used to identify the source of this logging channel; if it is
|
||||
* an Actor or ActorRef, its address is used, in case of a class an approximation of
|
||||
* its simpleName and in all other cases the simpleName of its class.
|
||||
* Java API: Obtain LoggingAdapter for the given system and source object. The
|
||||
* source object is used to identify the source of this logging channel. The source
|
||||
* object is translated to a String according to the following rules:
|
||||
* <ul>
|
||||
* <li>if it is an Actor or ActorRef, its path is used</li>
|
||||
* <li>in case of a String it is used as is</li>
|
||||
* <li>in case of a class an approximation of its simpleName
|
||||
* <li>and in all other cases the simpleName of its class</li>
|
||||
* </ul>
|
||||
*/
|
||||
def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = apply(system.eventStream, LogSource.fromAnyRef(logSource))
|
||||
|
||||
|
|
@ -354,6 +369,11 @@ object Logging {
|
|||
*/
|
||||
case object LoggerInitialized
|
||||
|
||||
/**
|
||||
* Java API to create a LoggerInitialized message.
|
||||
*/
|
||||
def loggerInitialized() = LoggerInitialized
|
||||
|
||||
class LoggerInitializationException(msg: String) extends AkkaException(msg)
|
||||
|
||||
trait StdOutLogger {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import annotation.target._
|
|||
* This annotation marks a feature which is not yet considered stable and may
|
||||
* change or be removed in a future release.
|
||||
*
|
||||
* @author Roland Kuhn
|
||||
* @since 1.2
|
||||
*/
|
||||
@getter
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ trait VersionedIterable[A] {
|
|||
|
||||
/**
|
||||
* Manages connections (ActorRefs) for a router.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait ConnectionManager {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ import scala.collection.mutable.{ Buffer, Map }
|
|||
* Consistent Hashing node ring abstraction.
|
||||
*
|
||||
* Not thread-safe, to be used from within an Actor or protected some other way.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ConsistentHash[T](nodes: Seq[T], replicas: Int) {
|
||||
private val cluster = Buffer[T]()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package akka.serialization
|
|||
import akka.actor.Actor
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
* trait Serializer extends scala.Serializable {
|
||||
* @volatile
|
||||
* var classLoader: Option[ClassLoader] = None
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@
|
|||
|
||||
package akka.util
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Convert {
|
||||
|
||||
def intToBytes(value: Int): Array[Byte] = {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ package akka.util
|
|||
|
||||
import java.security.{ MessageDigest, SecureRandom }
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Crypt {
|
||||
val hex = "0123456789ABCDEF"
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
|
|
|
|||
|
|
@ -38,7 +38,19 @@ case class Timer(duration: Duration, throwExceptionOnTimeout: Boolean = false) {
|
|||
}
|
||||
}
|
||||
|
||||
case class Deadline(d: Duration) {
|
||||
def +(other: Duration): Deadline = copy(d = d + other)
|
||||
def -(other: Duration): Deadline = copy(d = d - other)
|
||||
def -(other: Deadline): Duration = d - other.d
|
||||
def timeLeft: Duration = this - Deadline.now
|
||||
}
|
||||
object Deadline {
|
||||
def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS))
|
||||
}
|
||||
|
||||
object Duration {
|
||||
implicit def timeLeft(implicit d: Deadline): Duration = d.timeLeft
|
||||
|
||||
def apply(length: Long, unit: TimeUnit): Duration = new FiniteDuration(length, unit)
|
||||
def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length)
|
||||
def apply(length: Long, unit: String): Duration = new FiniteDuration(length, timeUnit(unit))
|
||||
|
|
@ -129,10 +141,7 @@ object Duration {
|
|||
override def *(factor: Double): Duration = throw new IllegalArgumentException("cannot multiply Undefined duration")
|
||||
override def /(factor: Double): Duration = throw new IllegalArgumentException("cannot divide Undefined duration")
|
||||
override def /(other: Duration): Double = throw new IllegalArgumentException("cannot divide Undefined duration")
|
||||
def >(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
|
||||
def >=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
|
||||
def <(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
|
||||
def <=(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
|
||||
def compare(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration")
|
||||
def unary_- : Duration = throw new IllegalArgumentException("cannot negate Undefined duration")
|
||||
}
|
||||
|
||||
|
|
@ -183,10 +192,7 @@ object Duration {
|
|||
*/
|
||||
val Inf: Duration = new Duration with Infinite {
|
||||
override def toString = "Duration.Inf"
|
||||
def >(other: Duration) = true
|
||||
def >=(other: Duration) = true
|
||||
def <(other: Duration) = false
|
||||
def <=(other: Duration) = false
|
||||
def compare(other: Duration) = 1
|
||||
def unary_- : Duration = MinusInf
|
||||
}
|
||||
|
||||
|
|
@ -196,10 +202,7 @@ object Duration {
|
|||
*/
|
||||
val MinusInf: Duration = new Duration with Infinite {
|
||||
override def toString = "Duration.MinusInf"
|
||||
def >(other: Duration) = false
|
||||
def >=(other: Duration) = false
|
||||
def <(other: Duration) = true
|
||||
def <=(other: Duration) = true
|
||||
def compare(other: Duration) = -1
|
||||
def unary_- : Duration = Inf
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +258,7 @@ object Duration {
|
|||
* val d3 = d2 + 1.millisecond
|
||||
* </pre>
|
||||
*/
|
||||
abstract class Duration extends Serializable {
|
||||
abstract class Duration extends Serializable with Ordered[Duration] {
|
||||
def length: Long
|
||||
def unit: TimeUnit
|
||||
def toNanos: Long
|
||||
|
|
@ -267,10 +270,6 @@ abstract class Duration extends Serializable {
|
|||
def toDays: Long
|
||||
def toUnit(unit: TimeUnit): Double
|
||||
def printHMS: String
|
||||
def <(other: Duration): Boolean
|
||||
def <=(other: Duration): Boolean
|
||||
def >(other: Duration): Boolean
|
||||
def >=(other: Duration): Boolean
|
||||
def +(other: Duration): Duration
|
||||
def -(other: Duration): Duration
|
||||
def *(factor: Double): Duration
|
||||
|
|
@ -281,6 +280,7 @@ abstract class Duration extends Serializable {
|
|||
def min(other: Duration): Duration = if (this < other) this else other
|
||||
def max(other: Duration): Duration = if (this > other) this else other
|
||||
def sleep(): Unit = Thread.sleep(toMillis)
|
||||
def fromNow: Deadline = Deadline.now + this
|
||||
|
||||
// Java API
|
||||
def lt(other: Duration) = this < other
|
||||
|
|
@ -329,37 +329,12 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
|
|||
|
||||
def printHMS = "%02d:%02d:%06.3f".format(toHours, toMinutes % 60, toMillis / 1000. % 60)
|
||||
|
||||
def <(other: Duration) = {
|
||||
def compare(other: Duration) =
|
||||
if (other.finite_?) {
|
||||
toNanos < other.asInstanceOf[FiniteDuration].toNanos
|
||||
} else {
|
||||
other > this
|
||||
}
|
||||
}
|
||||
|
||||
def <=(other: Duration) = {
|
||||
if (other.finite_?) {
|
||||
toNanos <= other.asInstanceOf[FiniteDuration].toNanos
|
||||
} else {
|
||||
other >= this
|
||||
}
|
||||
}
|
||||
|
||||
def >(other: Duration) = {
|
||||
if (other.finite_?) {
|
||||
toNanos > other.asInstanceOf[FiniteDuration].toNanos
|
||||
} else {
|
||||
other < this
|
||||
}
|
||||
}
|
||||
|
||||
def >=(other: Duration) = {
|
||||
if (other.finite_?) {
|
||||
toNanos >= other.asInstanceOf[FiniteDuration].toNanos
|
||||
} else {
|
||||
other <= this
|
||||
}
|
||||
}
|
||||
val me = toNanos
|
||||
val o = other.toNanos
|
||||
if (me > o) 1 else if (me < o) -1 else 0
|
||||
} else -other.compare(this)
|
||||
|
||||
def +(other: Duration) = {
|
||||
if (!other.finite_?) {
|
||||
|
|
@ -397,6 +372,8 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
|
|||
}
|
||||
|
||||
class DurationInt(n: Int) {
|
||||
import duration.Classifier
|
||||
|
||||
def nanoseconds = Duration(n, NANOSECONDS)
|
||||
def nanos = Duration(n, NANOSECONDS)
|
||||
def nanosecond = Duration(n, NANOSECONDS)
|
||||
|
|
@ -423,9 +400,38 @@ class DurationInt(n: Int) {
|
|||
|
||||
def days = Duration(n, DAYS)
|
||||
def day = Duration(n, DAYS)
|
||||
|
||||
def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
|
||||
def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
|
||||
def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
|
||||
def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS))
|
||||
def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS))
|
||||
|
||||
def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES))
|
||||
def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES))
|
||||
|
||||
def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS))
|
||||
def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS))
|
||||
|
||||
def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS))
|
||||
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS))
|
||||
}
|
||||
|
||||
class DurationLong(n: Long) {
|
||||
import duration.Classifier
|
||||
|
||||
def nanoseconds = Duration(n, NANOSECONDS)
|
||||
def nanos = Duration(n, NANOSECONDS)
|
||||
def nanosecond = Duration(n, NANOSECONDS)
|
||||
|
|
@ -452,9 +458,38 @@ class DurationLong(n: Long) {
|
|||
|
||||
def days = Duration(n, DAYS)
|
||||
def day = Duration(n, DAYS)
|
||||
|
||||
def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS))
|
||||
|
||||
def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS))
|
||||
|
||||
def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS))
|
||||
|
||||
def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS))
|
||||
def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS))
|
||||
|
||||
def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES))
|
||||
def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES))
|
||||
|
||||
def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS))
|
||||
def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS))
|
||||
|
||||
def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS))
|
||||
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS))
|
||||
}
|
||||
|
||||
class DurationDouble(d: Double) {
|
||||
import duration.Classifier
|
||||
|
||||
def nanoseconds = Duration(d, NANOSECONDS)
|
||||
def nanos = Duration(d, NANOSECONDS)
|
||||
def nanosecond = Duration(d, NANOSECONDS)
|
||||
|
|
@ -481,5 +516,32 @@ class DurationDouble(d: Double) {
|
|||
|
||||
def days = Duration(d, DAYS)
|
||||
def day = Duration(d, DAYS)
|
||||
|
||||
def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS))
|
||||
def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS))
|
||||
def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS))
|
||||
def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS))
|
||||
|
||||
def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS))
|
||||
def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS))
|
||||
def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS))
|
||||
def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS))
|
||||
|
||||
def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS))
|
||||
def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS))
|
||||
def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS))
|
||||
def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS))
|
||||
|
||||
def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS))
|
||||
def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS))
|
||||
|
||||
def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES))
|
||||
def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES))
|
||||
|
||||
def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS))
|
||||
def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS))
|
||||
|
||||
def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS))
|
||||
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import java.lang.{ Float ⇒ JFloat, Double ⇒ JDouble }
|
|||
* result
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object HashCode {
|
||||
val SEED = 23
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ import java.util.Comparator
|
|||
import scala.annotation.tailrec
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Helpers {
|
||||
|
||||
def makePattern(s: String): Pattern = Pattern.compile("^\\Q" + s.replace("?", "\\E.\\Q").replace("*", "\\E.*\\Q") + "\\E$")
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import scala.collection.mutable
|
|||
* An implementation of a ConcurrentMultiMap
|
||||
* Adds/remove is serialized over the specified key
|
||||
* Reads are fully concurrent <-- el-cheapo
|
||||
*
|
||||
* @author Viktor Klang
|
||||
*/
|
||||
class Index[K, V](val mapSize: Int, val valueComparator: Comparator[V]) {
|
||||
|
||||
|
|
@ -192,7 +190,5 @@ class Index[K, V](val mapSize: Int, val valueComparator: Comparator[V]) {
|
|||
* An implementation of a ConcurrentMultiMap
|
||||
* Adds/remove is serialized over the specified key
|
||||
* Reads are fully concurrent <-- el-cheapo
|
||||
*
|
||||
* @author Viktor Klang
|
||||
*/
|
||||
class ConcurrentMultiMap[K, V](mapSize: Int, valueComparator: Comparator[V]) extends Index[K, V](mapSize, valueComparator)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ import java.lang.management.ManagementFactory
|
|||
import javax.management.{ ObjectInstance, ObjectName, InstanceAlreadyExistsException, InstanceNotFoundException }
|
||||
import akka.actor.ActorSystem
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object JMX {
|
||||
private val mbeanServer = ManagementFactory.getPlatformMBeanServer
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import akka.actor.{ ActorInitializationException, ActorRef }
|
|||
|
||||
/**
|
||||
* A manager for listener actors. Intended for mixin by observables.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
trait ListenerManagement {
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ package akka.util
|
|||
import java.util.concurrent.locks.{ ReentrantLock }
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean }
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final class ReentrantGuard {
|
||||
final val lock = new ReentrantLock
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,23 @@ package akka.util
|
|||
import java.util.concurrent.TimeUnit
|
||||
|
||||
package object duration {
|
||||
trait Classifier[C] {
|
||||
type R
|
||||
def convert(d: Duration): R
|
||||
}
|
||||
|
||||
object span
|
||||
implicit object spanConvert extends Classifier[span.type] {
|
||||
type R = Duration
|
||||
def convert(d: Duration) = d
|
||||
}
|
||||
|
||||
object fromNow
|
||||
implicit object fromNowConvert extends Classifier[fromNow.type] {
|
||||
type R = Deadline
|
||||
def convert(d: Duration) = Deadline.now + d
|
||||
}
|
||||
|
||||
implicit def intToDurationInt(n: Int) = new DurationInt(n)
|
||||
implicit def longToDurationLong(n: Long) = new DurationLong(n)
|
||||
implicit def doubleToDurationDouble(d: Double) = new DurationDouble(d)
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
|||
|
||||
@Before
|
||||
def setUp{
|
||||
publisher = actorOf(new TypedConsumerPublisherMock)
|
||||
requestor = actorOf(new TypedConsumerPublishRequestor)
|
||||
publisher = actorOf(Props(new TypedConsumerPublisherMock)
|
||||
requestor = actorOf(Props(new TypedConsumerPublishRequestor)
|
||||
requestor ! InitPublishRequestor(publisher)
|
||||
consumer = actorOf(new Actor with Consumer {
|
||||
consumer = actorOf(Props(new Actor with Consumer {
|
||||
def endpointUri = "mock:test"
|
||||
protected def receive = null
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ class ConsumerPublishRequestorTest extends JUnitSuite {
|
|||
|
||||
@Before
|
||||
def setUp{
|
||||
publisher = actorOf(new ConsumerPublisherMock)
|
||||
requestor = actorOf(new ConsumerPublishRequestor)
|
||||
publisher = actorOf(Props(new ConsumerPublisherMock)
|
||||
requestor = actorOf(Props(new ConsumerPublishRequestor)
|
||||
requestor ! InitPublishRequestor(publisher)
|
||||
consumer = actorOf(new Actor with Consumer {
|
||||
consumer = actorOf(Props(new Actor with Consumer {
|
||||
def endpointUri = "mock:test"
|
||||
protected def receive = null
|
||||
}).asInstanceOf[LocalActorRef]
|
||||
|
|
|
|||
|
|
@ -9,21 +9,21 @@ class ConsumerRegisteredTest extends JUnitSuite {
|
|||
|
||||
@Test
|
||||
def shouldCreateSomeNonBlockingPublishRequestFromConsumer = {
|
||||
val c = Actor.actorOf[ConsumerActor1]
|
||||
val c = Actor.actorOf(Props[ConsumerActor1]
|
||||
val event = ConsumerActorRegistered.eventFor(c)
|
||||
assert(event === Some(ConsumerActorRegistered(c, consumerOf(c))))
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldCreateSomeBlockingPublishRequestFromConsumer = {
|
||||
val c = Actor.actorOf[ConsumerActor2]
|
||||
val c = Actor.actorOf(Props[ConsumerActor2]
|
||||
val event = ConsumerActorRegistered.eventFor(c)
|
||||
assert(event === Some(ConsumerActorRegistered(c, consumerOf(c))))
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldCreateNoneFromConsumer = {
|
||||
val event = ConsumerActorRegistered.eventFor(Actor.actorOf[PlainActor])
|
||||
val event = ConsumerActorRegistered.eventFor(Actor.actorOf(Props[PlainActor])
|
||||
assert(event === None)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
service = CamelServiceFactory.createCamelService
|
||||
// register test consumer before registering the publish requestor
|
||||
// and before starting the CamelService (registry is scanned for consumers)
|
||||
actorOf(new TestConsumer("direct:publish-test-1"))
|
||||
actorOf(Props(new TestConsumer("direct:publish-test-1"))
|
||||
service.registerPublishRequestor
|
||||
service.awaitEndpointActivation(1) {
|
||||
service.start
|
||||
|
|
@ -54,7 +54,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
"started" must {
|
||||
"support an in-out message exchange via its endpoint" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
consumer = actorOf(new TestConsumer("direct:publish-test-2"))
|
||||
consumer = actorOf(Props(new TestConsumer("direct:publish-test-2"))
|
||||
} must be(true)
|
||||
mandatoryTemplate.requestBody("direct:publish-test-2", "msg2") must equal("received msg2")
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
"activated with a custom error handler" must {
|
||||
"handle thrown exceptions by generating a custom response" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
actorOf[ErrorHandlingConsumer]
|
||||
actorOf(Props[ErrorHandlingConsumer]
|
||||
} must be(true)
|
||||
mandatoryTemplate.requestBody("direct:error-handler-test", "hello") must equal("error: hello")
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
"activated with a custom redelivery handler" must {
|
||||
"handle thrown exceptions by redelivering the initial message" in {
|
||||
service.awaitEndpointActivation(1) {
|
||||
actorOf[RedeliveringConsumer]
|
||||
actorOf(Props[RedeliveringConsumer]
|
||||
} must be(true)
|
||||
mandatoryTemplate.requestBody("direct:redelivery-test", "hello") must equal("accepted: hello")
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
var consumer: ActorRef = null
|
||||
|
||||
service.awaitEndpointActivation(1) {
|
||||
consumer = actorOf(new TestAckConsumer("direct:system-ack-test"))
|
||||
consumer = actorOf(Props(new TestAckConsumer("direct:system-ack-test"))
|
||||
} must be(true)
|
||||
|
||||
val endpoint = mandatoryContext.getEndpoint("direct:system-ack-test", classOf[DirectEndpoint])
|
||||
|
|
@ -169,19 +169,19 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
|
||||
"A supervised consumer" must {
|
||||
"be able to reply during receive" in {
|
||||
val consumer = Actor.actorOf(new SupervisedConsumer("reply-channel-test-1"))
|
||||
val consumer = Actor.actorOf(Props(new SupervisedConsumer("reply-channel-test-1"))
|
||||
(consumer ? "succeed").get must equal("ok")
|
||||
}
|
||||
|
||||
"be able to reply on failure during preRestart" in {
|
||||
val consumer = Actor.actorOf(new SupervisedConsumer("reply-channel-test-2"))
|
||||
val consumer = Actor.actorOf(Props(new SupervisedConsumer("reply-channel-test-2"))
|
||||
val supervisor = Supervisor(
|
||||
SupervisorConfig(
|
||||
OneForOneStrategy(List(classOf[Exception]), 2, 10000),
|
||||
Supervise(consumer, Permanent) :: Nil))
|
||||
|
||||
val latch = new CountDownLatch(1)
|
||||
val sender = Actor.actorOf(new Sender("pr", latch))
|
||||
val sender = Actor.actorOf(Props(new Sender("pr", latch))
|
||||
|
||||
consumer.!("fail")(Some(sender))
|
||||
latch.await(5, TimeUnit.SECONDS) must be(true)
|
||||
|
|
@ -195,7 +195,7 @@ class ConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMatcher
|
|||
Supervise(consumer, Temporary) :: Nil))
|
||||
|
||||
val latch = new CountDownLatch(1)
|
||||
val sender = Actor.actorOf(new Sender("ps", latch))
|
||||
val sender = Actor.actorOf(Props(new Sender("ps", latch))
|
||||
|
||||
consumer.!("fail")(Some(sender))
|
||||
latch.await(5, TimeUnit.SECONDS) must be(true)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message and receive normal response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-2", true))
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-2", true))
|
||||
|
||||
when("a test message is sent to the producer with ?")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -44,7 +44,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message and receive failure response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-2"))
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-2"))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with ?")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -59,7 +59,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message oneway") {
|
||||
given("a registered one-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-1", true) with Oneway)
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-1", true) with Oneway)
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("TEST")
|
||||
|
|
@ -71,7 +71,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message twoway without sender reference") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-1"))
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-1"))
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("test")
|
||||
|
|
@ -86,7 +86,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message and receive normal response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-3"))
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-3"))
|
||||
|
||||
when("a test message is sent to the producer with ?")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -98,7 +98,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message and receive failure response") {
|
||||
given("a registered two-way producer")
|
||||
val producer = actorOf(new TestProducer("direct:producer-test-3"))
|
||||
val producer = actorOf(Props(new TestProducer("direct:producer-test-3"))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with ?")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -116,8 +116,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward normal response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target))
|
||||
val target = actorOf(Props[ReplyingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-2", target))
|
||||
|
||||
when("a test message is sent to the producer with ?")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -130,8 +130,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward failure response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target))
|
||||
val target = actorOf(Props[ReplyingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-2", target))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with ?")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -146,8 +146,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target))
|
||||
val target = actorOf(Props[ProducingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-2", target))
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("received test")
|
||||
|
|
@ -159,8 +159,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-2", target))
|
||||
val target = actorOf(Props[ProducingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-2", target))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !")
|
||||
mockEndpoint.expectedMessageCount(1)
|
||||
|
|
@ -176,8 +176,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward normal response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target))
|
||||
val target = actorOf(Props[ReplyingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-3", target))
|
||||
|
||||
when("a test message is sent to the producer with ?")
|
||||
val message = Message("test", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -190,8 +190,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward failure response to a replying target actor and receive response") {
|
||||
given("a registered two-way producer configured with a forward target")
|
||||
val target = actorOf[ReplyingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target))
|
||||
val target = actorOf(Props[ReplyingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-3", target))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with ?")
|
||||
val message = Message("fail", Map(Message.MessageExchangeId -> "123"))
|
||||
|
|
@ -206,8 +206,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward normal response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target))
|
||||
val target = actorOf(Props[ProducingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-3", target))
|
||||
|
||||
when("a test message is sent to the producer with !")
|
||||
mockEndpoint.expectedBodiesReceived("received test")
|
||||
|
|
@ -219,8 +219,8 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before
|
|||
|
||||
scenario("produce message, forward failure response to a producing target actor and produce response to direct:forward-test-1") {
|
||||
given("a registered one-way producer configured with a forward target")
|
||||
val target = actorOf[ProducingForwardTarget]
|
||||
val producer = actorOf(new TestForwarder("direct:producer-test-3", target))
|
||||
val target = actorOf(Props[ProducingForwardTarget]
|
||||
val producer = actorOf(Props(new TestForwarder("direct:producer-test-3", target))
|
||||
|
||||
when("a test message causing an exception is sent to the producer with !")
|
||||
mockEndpoint.expectedMessageCount(1)
|
||||
|
|
@ -271,7 +271,7 @@ object ProducerFeatureTest {
|
|||
}
|
||||
|
||||
class TestRoute extends RouteBuilder {
|
||||
val responder = actorOf[TestResponder]
|
||||
val responder = actorOf(Props[TestResponder]
|
||||
def configure {
|
||||
from("direct:forward-test-1").to("mock:mock")
|
||||
// for one-way messaging tests
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with
|
|||
import CamelContextManager.mandatoryTemplate
|
||||
|
||||
scenario("one-way communication") {
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
mandatoryTemplate.sendBody("actor:uuid:%s" format actor.uuid, "Martin")
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
|
@ -42,7 +42,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with
|
|||
}
|
||||
|
||||
scenario("two-way communication") {
|
||||
val actor = actorOf[Tester2]
|
||||
val actor = actorOf(Props[Tester2]
|
||||
assert(mandatoryTemplate.requestBody("actor:uuid:%s" format actor.uuid, "Martin") === "Hello Martin")
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with
|
|||
import CamelContextManager.mandatoryTemplate
|
||||
|
||||
scenario("one-way communication") {
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
mandatoryTemplate.sendBody("actor:%s" format actor.address, "Martin")
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
|
@ -79,12 +79,12 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with
|
|||
}
|
||||
|
||||
scenario("two-way communication") {
|
||||
val actor = actorOf[Tester2]
|
||||
val actor = actorOf(Props[Tester2]
|
||||
assert(mandatoryTemplate.requestBody("actor:%s" format actor.address, "Martin") === "Hello Martin")
|
||||
}
|
||||
|
||||
scenario("two-way communication via a custom route") {
|
||||
val actor = actorOf[CustomIdActor]("custom-id")
|
||||
val actor = actorOf(Props[CustomIdActor]("custom-id")
|
||||
assert(mandatoryTemplate.requestBody("direct:custom-id-test-1", "Martin") === "Received Martin")
|
||||
assert(mandatoryTemplate.requestBody("direct:custom-id-test-2", "Martin") === "Received Martin")
|
||||
}
|
||||
|
|
@ -113,8 +113,8 @@ object ActorComponentFeatureTest {
|
|||
}
|
||||
|
||||
class TestRoute extends RouteBuilder {
|
||||
val failWithMessage = actorOf[FailWithMessage]
|
||||
val failWithException = actorOf[FailWithException]
|
||||
val failWithMessage = actorOf(Props[FailWithMessage]
|
||||
val failWithException = actorOf(Props[FailWithException]
|
||||
def configure {
|
||||
from("direct:custom-id-test-1").to("actor:custom-id")
|
||||
from("direct:custom-id-test-2").to("actor:id:custom-id")
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorWithSyncProcessor = {
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
|
|
@ -38,7 +38,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorWithAsyncProcessor = {
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
val exchange = endpoint.createExchange(ExchangePattern.InOnly)
|
||||
|
|
@ -53,7 +53,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorAndReceiveResponseWithSyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
val actor = actorOf(Props(new Tester2 {
|
||||
override def response(msg: Message) = Message(super.response(msg), Map("k2" -> "v2"))
|
||||
})
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor.uuid)
|
||||
|
|
@ -67,7 +67,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorAndReceiveResponseWithAsyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
val actor = actorOf(Props(new Tester2 {
|
||||
override def response(msg: Message) = Message(super.response(msg), Map("k2" -> "v2"))
|
||||
})
|
||||
val completion = expectAsyncCompletion
|
||||
|
|
@ -83,7 +83,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorAndReceiveFailureWithAsyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
val actor = actorOf(Props(new Tester2 {
|
||||
override def response(msg: Message) = Failure(new Exception("testmsg"), Map("k3" -> "v3"))
|
||||
})
|
||||
val completion = expectAsyncCompletion
|
||||
|
|
@ -100,7 +100,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldSendMessageToActorAndReceiveAckWithAsyncProcessor = {
|
||||
val actor = actorOf(new Tester2 {
|
||||
val actor = actorOf(Props(new Tester2 {
|
||||
override def response(msg: Message) = akka.camel.Ack
|
||||
})
|
||||
val completion = expectAsyncCompletion
|
||||
|
|
@ -115,8 +115,8 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldDynamicallyRouteMessageToActorWithDefaultId = {
|
||||
val actor1 = actorOf[Tester1]("x")
|
||||
val actor2 = actorOf[Tester1]("y")
|
||||
val actor1 = actorOf(Props[Tester1]("x")
|
||||
val actor2 = actorOf(Props[Tester1]("y")
|
||||
actor1
|
||||
actor2
|
||||
val latch1 = (actor1 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
|
|
@ -139,8 +139,8 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldDynamicallyRouteMessageToActorWithoutDefaultId = {
|
||||
val actor1 = actorOf[Tester1]("x")
|
||||
val actor2 = actorOf[Tester1]("y")
|
||||
val actor1 = actorOf(Props[Tester1]("x")
|
||||
val actor2 = actorOf(Props[Tester1]("y")
|
||||
actor1
|
||||
actor2
|
||||
val latch1 = (actor1 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
|
|
@ -164,8 +164,8 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldDynamicallyRouteMessageToActorWithDefaultUuid = {
|
||||
val actor1 = actorOf[Tester1]
|
||||
val actor2 = actorOf[Tester1]
|
||||
val actor1 = actorOf(Props[Tester1]
|
||||
val actor2 = actorOf(Props[Tester1]
|
||||
val latch1 = (actor1 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:%s" format actor1.uuid)
|
||||
|
|
@ -186,8 +186,8 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldDynamicallyRouteMessageToActorWithoutDefaultUuid = {
|
||||
val actor1 = actorOf[Tester1]
|
||||
val actor2 = actorOf[Tester1]
|
||||
val actor1 = actorOf(Props[Tester1]
|
||||
val actor2 = actorOf(Props[Tester1]
|
||||
val latch1 = (actor1 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val latch2 = (actor2 ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:")
|
||||
|
|
@ -209,7 +209,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldThrowExceptionWhenIdNotSet{
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:id:")
|
||||
intercept[ActorIdentifierNotSetException] {
|
||||
|
|
@ -219,7 +219,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll {
|
|||
|
||||
@Test
|
||||
def shouldThrowExceptionWhenUuidNotSet{
|
||||
val actor = actorOf[Tester1]
|
||||
val actor = actorOf(Props[Tester1]
|
||||
val latch = (actor ? SetExpectedMessageCount(1)).as[CountDownLatch].get
|
||||
val endpoint = actorEndpoint("actor:uuid:")
|
||||
intercept[ActorIdentifierNotSetException] {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ entry number it will use MAX_INTEGER). Once all the entries have been processed,
|
|||
new one for its use.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object BookKeeperServer {
|
||||
val port = 3181
|
||||
val zkServers = "localhost:2181"
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ import com.google.protobuf.ByteString
|
|||
|
||||
/**
|
||||
* JMX MBean for the cluster service.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait ClusterNodeMBean {
|
||||
|
||||
|
|
@ -140,8 +138,6 @@ trait ClusterNodeMBean {
|
|||
|
||||
/**
|
||||
* Module for the Cluster. Also holds global state such as configuration data etc.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Cluster {
|
||||
val EMPTY_STRING = "".intern
|
||||
|
|
@ -257,8 +253,6 @@ object Cluster {
|
|||
*
|
||||
* /clusterName/'actor-address-to-uuids'/actorAddress/actorUuid
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class DefaultClusterNode private[akka] (
|
||||
val nodeAddress: NodeAddress,
|
||||
|
|
@ -1601,9 +1595,6 @@ class DefaultClusterNode private[akka] (
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class MembershipChildListener(self: ClusterNode) extends IZkChildListener with ErrorHandler {
|
||||
def handleChildChange(parentPath: String, currentChilds: JList[String]) {
|
||||
withErrorHandler {
|
||||
|
|
@ -1643,9 +1634,6 @@ class MembershipChildListener(self: ClusterNode) extends IZkChildListener with E
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class StateListener(self: ClusterNode) extends IZkStateListener {
|
||||
def handleStateChanged(state: KeeperState) {
|
||||
state match {
|
||||
|
|
@ -1671,9 +1659,6 @@ class StateListener(self: ClusterNode) extends IZkStateListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait ErrorHandler {
|
||||
def withErrorHandler[T](body: ⇒ T) = {
|
||||
try {
|
||||
|
|
@ -1686,9 +1671,6 @@ trait ErrorHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object RemoteClusterDaemon {
|
||||
val Address = "akka-cluster-daemon".intern
|
||||
|
||||
|
|
@ -1700,8 +1682,6 @@ object RemoteClusterDaemon {
|
|||
* Internal "daemon" actor for cluster internal communication.
|
||||
*
|
||||
* It acts as the brain of the cluster that responds to cluster events (messages) and undertakes action.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import annotation.tailrec
|
|||
|
||||
/**
|
||||
* ClusterActorRef factory and locator.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object ClusterActorRef {
|
||||
import FailureDetectorType._
|
||||
|
|
@ -77,8 +75,6 @@ object ClusterActorRef {
|
|||
/**
|
||||
* ActorRef representing a one or many instances of a clustered, load-balanced and sometimes replicated actor
|
||||
* where the instances can reside on other nodes in the cluster.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] class ClusterActorRef(props: RoutedProps, val address: String) extends AbstractRoutedActorRef(props) {
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,6 @@ import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
|||
|
||||
/**
|
||||
* A ClusterDeployer is responsible for deploying a Deploy.
|
||||
*
|
||||
* FIXME Document: what does Deploy mean?
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object ClusterDeployer extends ActorDeployer {
|
||||
val clusterName = Cluster.name
|
||||
|
|
|
|||
|
|
@ -26,19 +26,12 @@ import java.util.Enumeration
|
|||
// FIXME allow user to choose dynamically between 'async' and 'sync' tx logging (asyncAddEntry(byte[] data, AddCallback cb, Object ctx))
|
||||
// FIXME clean up old entries in log after doing a snapshot
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ReplicationException(message: String, cause: Throwable = null) extends AkkaException(message) {
|
||||
def this(msg: String) = this(msg, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Explain something about threadsafety.
|
||||
*
|
||||
* A TransactionLog makes chunks of data durable.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class TransactionLog private (
|
||||
ledger: LedgerHandle,
|
||||
|
|
@ -352,7 +345,7 @@ class TransactionLog private (
|
|||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
* TODO: Documentation.
|
||||
*/
|
||||
object TransactionLog {
|
||||
|
||||
|
|
@ -563,8 +556,6 @@ object TransactionLog {
|
|||
|
||||
/**
|
||||
* TODO: Documentation.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object LocalBookKeeperEnsemble {
|
||||
private val isRunning = new Switch(false)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class ClusterActorRefCleanupMultiJvmNode1 extends MasterClusterTestNode {
|
|||
Cluster.node.start()
|
||||
barrier("awaitStarted", NrOfNodes).await()
|
||||
|
||||
val ref = Actor.actorOf[ClusterActorRefCleanupMultiJvmSpec.TestActor]("service-test")
|
||||
val ref = Actor.actorOf(Props[ClusterActorRefCleanupMultiJvmSpec.TestActor]("service-test")
|
||||
|
||||
ref.isInstanceOf[ClusterActorRef] must be(true)
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
// }
|
||||
|
||||
// barrier("create-actor-on-node1", NrOfNodes) {
|
||||
// val actorRef = Actor.actorOf[HelloWorld]("hello-world-write-behind-nosnapshot")
|
||||
// val actorRef = Actor.actorOf(Props[HelloWorld]("hello-world-write-behind-nosnapshot")
|
||||
// // node.isInUseOnNode("hello-world") must be(true)
|
||||
// actorRef.address must be("hello-world-write-behind-nosnapshot")
|
||||
// for (i ← 0 until 10) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
// }
|
||||
|
||||
// barrier("create-actor-on-node1", NrOfNodes) {
|
||||
// val actorRef = Actor.actorOf[HelloWorld]("hello-world-write-behind-snapshot")
|
||||
// val actorRef = Actor.actorOf(Props[HelloWorld]("hello-world-write-behind-snapshot")
|
||||
// node.isInUseOnNode("hello-world-write-behind-snapshot") must be(true)
|
||||
// actorRef.address must be("hello-world-write-behind-snapshot")
|
||||
// var counter = 0
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
// }
|
||||
|
||||
// barrier("create-actor-on-node1", NrOfNodes) {
|
||||
// val actorRef = Actor.actorOf[HelloWorld]("hello-world-write-through-nosnapshot")
|
||||
// val actorRef = Actor.actorOf(Props[HelloWorld]("hello-world-write-through-nosnapshot")
|
||||
// actorRef.address must be("hello-world-write-through-nosnapshot")
|
||||
// for (i ← 0 until 10)
|
||||
// (actorRef ? Count(i)).as[String] must be(Some("World from node [node1]"))
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
// }
|
||||
|
||||
// barrier("create-actor-on-node1", NrOfNodes) {
|
||||
// val actorRef = Actor.actorOf[HelloWorld]("hello-world-write-through-snapshot")
|
||||
// val actorRef = Actor.actorOf(Props[HelloWorld]("hello-world-write-through-snapshot")
|
||||
// node.isInUseOnNode("hello-world-write-through-snapshot") must be(true)
|
||||
// actorRef.address must be("hello-world-write-through-snapshot")
|
||||
// var counter = 0
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class DirectRoutingFailoverMultiJvmNode1 extends MasterClusterTestNode {
|
|||
}
|
||||
|
||||
LocalCluster.barrier("actor-creation", NrOfNodes) {
|
||||
actor = Actor.actorOf[SomeActor]("service-hello")
|
||||
actor = Actor.actorOf(Props[SomeActor]("service-hello")
|
||||
}
|
||||
|
||||
LocalCluster.barrier("verify-actor", NrOfNodes) {
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ class HomeNodeMultiJvmNode2 extends ClusterTestNode {
|
|||
Cluster.node.start()
|
||||
barrier("waiting-for-begin", NrOfNodes).await()
|
||||
|
||||
val actorNode1 = Actor.actorOf[SomeActor]("service-node1")
|
||||
val actorNode1 = Actor.actorOf(Props[SomeActor]("service-node1")
|
||||
val name1 = (actorNode1 ? "identify").get.asInstanceOf[String]
|
||||
name1 must equal("node1")
|
||||
|
||||
val actorNode2 = Actor.actorOf[SomeActor]("service-node2")
|
||||
val actorNode2 = Actor.actorOf(Props[SomeActor]("service-node2")
|
||||
val name2 = (actorNode2 ? "identify").get.asInstanceOf[String]
|
||||
name2 must equal("node2")
|
||||
|
||||
|
|
|
|||
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