2012-09-12 11:18:42 +02:00
|
|
|
/**
|
2016-02-23 12:58:39 +01:00
|
|
|
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
2012-09-12 11:18:42 +02:00
|
|
|
*/
|
|
|
|
|
package akka.remote
|
|
|
|
|
|
|
|
|
|
import akka.actor._
|
2015-02-17 13:40:48 +01:00
|
|
|
import akka.event.AddressTerminatedTopic
|
2012-09-12 11:18:42 +02:00
|
|
|
import akka.pattern.ask
|
2016-02-22 20:18:15 +01:00
|
|
|
import akka.remote.transport.AssociationHandle.{ HandleEventListener, HandleEvent }
|
2015-02-17 13:40:48 +01:00
|
|
|
import akka.remote.transport._
|
2015-09-23 14:31:26 +02:00
|
|
|
import akka.remote.transport.Transport.InvalidAssociationException
|
2012-09-12 11:18:42 +02:00
|
|
|
import akka.testkit._
|
2013-03-20 20:38:49 +13:00
|
|
|
import akka.util.ByteString
|
2012-09-12 11:18:42 +02:00
|
|
|
import com.typesafe.config._
|
2013-03-20 20:38:49 +13:00
|
|
|
import java.io.NotSerializableException
|
2012-09-12 11:18:42 +02:00
|
|
|
import scala.concurrent.Await
|
|
|
|
|
import scala.concurrent.Future
|
|
|
|
|
import scala.concurrent.duration._
|
2015-09-25 08:39:02 +02:00
|
|
|
import java.util.concurrent.ThreadLocalRandom
|
2015-04-01 14:03:06 +02:00
|
|
|
import akka.testkit.SocketUtil.temporaryServerAddress
|
2012-09-12 11:18:42 +02:00
|
|
|
|
|
|
|
|
object RemotingSpec {
|
2013-03-26 18:17:50 +01:00
|
|
|
|
2014-03-07 13:20:01 +01:00
|
|
|
final case class ActorForReq(s: String)
|
|
|
|
|
final case class ActorSelReq(s: String)
|
2013-03-26 18:17:50 +01:00
|
|
|
|
2013-01-25 15:03:52 +01:00
|
|
|
class Echo1 extends Actor {
|
2012-09-12 11:18:42 +02:00
|
|
|
var target: ActorRef = context.system.deadLetters
|
|
|
|
|
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case (p: Props, n: String) ⇒ sender() ! context.actorOf(Props[Echo1], n)
|
2012-09-12 11:18:42 +02:00
|
|
|
case ex: Exception ⇒ throw ex
|
2014-01-16 15:16:35 +01:00
|
|
|
case ActorForReq(s) ⇒ sender() ! context.actorFor(s)
|
|
|
|
|
case ActorSelReq(s) ⇒ sender() ! context.actorSelection(s)
|
|
|
|
|
case x ⇒ target = sender(); sender() ! x
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def preStart() {}
|
|
|
|
|
override def preRestart(cause: Throwable, msg: Option[Any]) {
|
|
|
|
|
target ! "preRestart"
|
|
|
|
|
}
|
|
|
|
|
override def postRestart(cause: Throwable) {}
|
|
|
|
|
override def postStop() {
|
|
|
|
|
target ! "postStop"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 15:03:52 +01:00
|
|
|
class Echo2 extends Actor {
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case "ping" ⇒ sender() ! (("pong", sender()))
|
|
|
|
|
case a: ActorRef ⇒ a ! (("ping", sender()))
|
|
|
|
|
case ("ping", a: ActorRef) ⇒ sender() ! (("pong", a))
|
|
|
|
|
case ("pong", a: ActorRef) ⇒ a ! (("pong", sender().path.toSerializationFormat))
|
2013-01-25 15:03:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
class Proxy(val one: ActorRef, val another: ActorRef) extends Actor {
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case s if sender().path == one.path ⇒ another ! s
|
|
|
|
|
case s if sender().path == another.path ⇒ one ! s
|
2013-03-25 08:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val cfg: Config = ConfigFactory parseString (s"""
|
2012-09-12 11:18:42 +02:00
|
|
|
common-ssl-settings {
|
2013-03-25 08:42:48 +01:00
|
|
|
key-store = "${getClass.getClassLoader.getResource("keystore").getPath}"
|
|
|
|
|
trust-store = "${getClass.getClassLoader.getResource("truststore").getPath}"
|
2012-09-12 11:18:42 +02:00
|
|
|
key-store-password = "changeme"
|
2013-04-11 13:14:48 +02:00
|
|
|
key-password = "changeme"
|
2012-09-12 11:18:42 +02:00
|
|
|
trust-store-password = "changeme"
|
2015-12-18 11:47:19 +01:00
|
|
|
protocol = "TLSv1.2"
|
2012-09-12 11:18:42 +02:00
|
|
|
random-number-generator = "AES128CounterSecureRNG"
|
|
|
|
|
enabled-algorithms = [TLS_RSA_WITH_AES_128_CBC_SHA]
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 10:07:48 +01:00
|
|
|
common-netty-settings {
|
|
|
|
|
port = 0
|
|
|
|
|
hostname = "localhost"
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-12 11:18:42 +02:00
|
|
|
akka {
|
2016-06-10 15:04:13 +02:00
|
|
|
actor.provider = remote
|
2013-03-21 10:07:48 +01:00
|
|
|
|
|
|
|
|
remote {
|
2013-04-09 13:07:39 +02:00
|
|
|
retry-gate-closed-for = 1 s
|
2013-03-21 10:07:48 +01:00
|
|
|
log-remote-lifecycle-events = on
|
|
|
|
|
|
|
|
|
|
enabled-transports = [
|
|
|
|
|
"akka.remote.test",
|
|
|
|
|
"akka.remote.netty.tcp",
|
|
|
|
|
"akka.remote.netty.udp",
|
|
|
|
|
"akka.remote.netty.ssl"
|
|
|
|
|
]
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
netty.tcp = $${common-netty-settings}
|
|
|
|
|
netty.udp = $${common-netty-settings}
|
|
|
|
|
netty.ssl = $${common-netty-settings}
|
|
|
|
|
netty.ssl.security = $${common-ssl-settings}
|
2013-03-21 10:07:48 +01:00
|
|
|
|
|
|
|
|
test {
|
2012-09-12 11:18:42 +02:00
|
|
|
transport-class = "akka.remote.transport.TestTransport"
|
2012-11-23 10:15:19 +01:00
|
|
|
applied-adapters = []
|
|
|
|
|
registry-key = aX33k0jWKg
|
|
|
|
|
local-address = "test://RemotingSpec@localhost:12345"
|
|
|
|
|
maximum-payload-bytes = 32000 bytes
|
|
|
|
|
scheme-identifier = test
|
2013-03-21 10:07:48 +01:00
|
|
|
}
|
2012-11-23 10:15:19 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
|
|
|
|
|
actor.deployment {
|
2013-01-24 04:28:21 -08:00
|
|
|
/blub.remote = "akka.test://remote-sys@localhost:12346"
|
2013-03-26 18:17:50 +01:00
|
|
|
/looker1/child.remote = "akka.test://remote-sys@localhost:12346"
|
|
|
|
|
/looker1/child/grandchild.remote = "akka.test://RemotingSpec@localhost:12345"
|
|
|
|
|
/looker2/child.remote = "akka.test://remote-sys@localhost:12346"
|
|
|
|
|
/looker2/child/grandchild.remote = "akka.test://RemotingSpec@localhost:12345"
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-03-25 08:42:48 +01:00
|
|
|
""")
|
2012-09-12 11:18:42 +02:00
|
|
|
|
2014-04-02 14:48:32 +02:00
|
|
|
def muteSystem(system: ActorSystem) {
|
|
|
|
|
system.eventStream.publish(TestEvent.Mute(
|
|
|
|
|
EventFilter.error(start = "AssociationError"),
|
|
|
|
|
EventFilter.warning(start = "AssociationError"),
|
|
|
|
|
EventFilter.warning(pattern = "received dead letter.*")))
|
|
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class RemotingSpec extends AkkaSpec(RemotingSpec.cfg) with ImplicitSender with DefaultTimeout {
|
|
|
|
|
|
|
|
|
|
import RemotingSpec._
|
|
|
|
|
|
|
|
|
|
val conf = ConfigFactory.parseString(
|
|
|
|
|
"""
|
2013-03-20 20:38:49 +13:00
|
|
|
akka.remote.test {
|
|
|
|
|
local-address = "test://remote-sys@localhost:12346"
|
|
|
|
|
maximum-payload-bytes = 48000 bytes
|
2012-11-23 10:15:19 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
""").withFallback(system.settings.config).resolve()
|
2013-03-25 08:42:48 +01:00
|
|
|
val remoteSystem = ActorSystem("remote-sys", conf)
|
2012-09-12 11:18:42 +02:00
|
|
|
|
2013-01-03 12:29:30 +01:00
|
|
|
for (
|
|
|
|
|
(name, proto) ← Seq(
|
2016-06-02 14:06:57 +02:00
|
|
|
"/gonk" → "tcp",
|
|
|
|
|
"/zagzag" → "udp",
|
|
|
|
|
"/roghtaar" → "ssl.tcp")
|
2013-03-25 08:42:48 +01:00
|
|
|
) deploy(system, Deploy(name, scope = RemoteScope(addr(remoteSystem, proto))))
|
2013-01-03 12:29:30 +01:00
|
|
|
|
|
|
|
|
def addr(sys: ActorSystem, proto: String) =
|
2013-01-24 04:28:21 -08:00
|
|
|
sys.asInstanceOf[ExtendedActorSystem].provider.getExternalAddressFor(Address(s"akka.$proto", "", "", 0)).get
|
2013-01-03 12:29:30 +01:00
|
|
|
def port(sys: ActorSystem, proto: String) = addr(sys, proto).port.get
|
|
|
|
|
def deploy(sys: ActorSystem, d: Deploy) {
|
|
|
|
|
sys.asInstanceOf[ExtendedActorSystem].provider.asInstanceOf[RemoteActorRefProvider].deployer.deploy(d)
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
val remote = remoteSystem.actorOf(Props[Echo2], "echo")
|
2012-09-12 11:18:42 +02:00
|
|
|
|
2013-01-24 04:28:21 -08:00
|
|
|
val here = system.actorFor("akka.test://remote-sys@localhost:12346/user/echo")
|
2012-09-12 11:18:42 +02:00
|
|
|
|
2013-03-20 20:38:49 +13:00
|
|
|
private def verifySend(msg: Any)(afterSend: ⇒ Unit) {
|
2013-05-09 20:43:06 +02:00
|
|
|
val bigBounceId = s"bigBounce-${ThreadLocalRandom.current.nextInt()}"
|
2013-03-25 08:42:48 +01:00
|
|
|
val bigBounceOther = remoteSystem.actorOf(Props(new Actor {
|
2013-03-20 20:38:49 +13:00
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case x: Int ⇒ sender() ! byteStringOfSize(x)
|
|
|
|
|
case x ⇒ sender() ! x
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
2013-05-29 16:13:10 +02:00
|
|
|
}).withDeploy(Deploy.local), bigBounceId)
|
2013-05-09 20:43:06 +02:00
|
|
|
val bigBounceHere = system.actorFor(s"akka.test://remote-sys@localhost:12346/user/$bigBounceId")
|
2013-03-20 20:38:49 +13:00
|
|
|
|
|
|
|
|
val eventForwarder = system.actorOf(Props(new Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case x ⇒ testActor ! x
|
|
|
|
|
}
|
2013-05-29 16:13:10 +02:00
|
|
|
}).withDeploy(Deploy.local))
|
2013-03-20 20:38:49 +13:00
|
|
|
system.eventStream.subscribe(eventForwarder, classOf[AssociationErrorEvent])
|
|
|
|
|
system.eventStream.subscribe(eventForwarder, classOf[DisassociatedEvent])
|
|
|
|
|
try {
|
|
|
|
|
bigBounceHere ! msg
|
|
|
|
|
afterSend
|
|
|
|
|
expectNoMsg(500.millis.dilated)
|
|
|
|
|
} finally {
|
|
|
|
|
system.eventStream.unsubscribe(eventForwarder, classOf[AssociationErrorEvent])
|
|
|
|
|
system.eventStream.unsubscribe(eventForwarder, classOf[DisassociatedEvent])
|
2013-05-09 20:43:06 +02:00
|
|
|
eventForwarder ! PoisonPill
|
|
|
|
|
bigBounceOther ! PoisonPill
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
override def atStartup() = {
|
2014-04-02 14:48:32 +02:00
|
|
|
muteSystem(system);
|
2013-03-25 08:42:48 +01:00
|
|
|
remoteSystem.eventStream.publish(TestEvent.Mute(
|
|
|
|
|
EventFilter[EndpointException](),
|
|
|
|
|
EventFilter.error(start = "AssociationError"),
|
|
|
|
|
EventFilter.warning(pattern = "received dead letter.*(InboundPayload|Disassociate|HandleListener)")))
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 20:38:49 +13:00
|
|
|
private def byteStringOfSize(size: Int) = ByteString.fromArray(Array.fill(size)(42: Byte))
|
|
|
|
|
|
|
|
|
|
val maxPayloadBytes = system.settings.config.getBytes("akka.remote.test.maximum-payload-bytes").toInt
|
|
|
|
|
|
2013-01-03 17:17:12 +01:00
|
|
|
override def afterTermination() {
|
2013-05-02 17:12:36 +02:00
|
|
|
shutdown(remoteSystem)
|
2012-09-12 11:18:42 +02:00
|
|
|
AssociationRegistry.clear()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"Remoting" must {
|
|
|
|
|
|
|
|
|
|
"support remote look-ups" in {
|
|
|
|
|
here ! "ping"
|
2012-11-23 10:15:19 +01:00
|
|
|
expectMsg(("pong", testActor))
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-26 15:41:05 +02:00
|
|
|
"send warning message for wrong address" in {
|
2013-10-09 11:29:02 +02:00
|
|
|
filterEvents(EventFilter.warning(pattern = "Address is now gated for ", occurrences = 1)) {
|
2013-08-26 15:41:05 +02:00
|
|
|
system.actorFor("akka.test://nonexistingsystem@localhost:12346/user/echo") ! "ping"
|
|
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"support ask" in {
|
|
|
|
|
Await.result(here ? "ping", timeout.duration) match {
|
|
|
|
|
case ("pong", s: akka.pattern.PromiseActorRef) ⇒ // good
|
|
|
|
|
case m ⇒ fail(m + " was not (pong, AskActorRef)")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"send dead letters on remote if actor does not exist" in {
|
|
|
|
|
EventFilter.warning(pattern = "dead.*buh", occurrences = 1).intercept {
|
2013-01-24 04:28:21 -08:00
|
|
|
system.actorFor("akka.test://remote-sys@localhost:12346/does/not/exist") ! "buh"
|
2013-03-25 08:42:48 +01:00
|
|
|
}(remoteSystem)
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
2013-01-25 15:03:52 +01:00
|
|
|
"not be exhausted by sending to broken connections" in {
|
2013-03-21 10:07:48 +01:00
|
|
|
val tcpOnlyConfig = ConfigFactory.parseString("""akka.remote.enabled-transports = ["akka.remote.netty.tcp"]""").
|
2013-03-25 08:42:48 +01:00
|
|
|
withFallback(remoteSystem.settings.config)
|
|
|
|
|
val moreSystems = Vector.fill(5)(ActorSystem(remoteSystem.name, tcpOnlyConfig))
|
|
|
|
|
moreSystems foreach { sys ⇒
|
|
|
|
|
sys.eventStream.publish(TestEvent.Mute(
|
|
|
|
|
EventFilter[EndpointDisassociatedException](),
|
|
|
|
|
EventFilter.warning(pattern = "received dead letter.*")))
|
|
|
|
|
sys.actorOf(Props[Echo2], name = "echo")
|
|
|
|
|
}
|
2013-03-26 18:17:50 +01:00
|
|
|
val moreRefs = moreSystems map (sys ⇒ system.actorSelection(RootActorPath(addr(sys, "tcp")) / "user" / "echo"))
|
|
|
|
|
val aliveEcho = system.actorSelection(RootActorPath(addr(remoteSystem, "tcp")) / "user" / "echo")
|
2013-01-25 15:03:52 +01:00
|
|
|
val n = 100
|
|
|
|
|
|
|
|
|
|
// first everything is up and running
|
|
|
|
|
1 to n foreach { x ⇒
|
|
|
|
|
aliveEcho ! "ping"
|
|
|
|
|
moreRefs(x % moreSystems.size) ! "ping"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
within(5.seconds) {
|
2015-01-16 11:09:59 +01:00
|
|
|
receiveN(n * 2) foreach { reply ⇒ reply should ===(("pong", testActor)) }
|
2013-01-25 15:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// then we shutdown all but one system to simulate broken connections
|
|
|
|
|
moreSystems foreach { sys ⇒
|
2013-05-02 17:12:36 +02:00
|
|
|
shutdown(sys)
|
2013-01-25 15:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
1 to n foreach { x ⇒
|
|
|
|
|
aliveEcho ! "ping"
|
|
|
|
|
moreRefs(x % moreSystems.size) ! "ping"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ping messages to aliveEcho should go through even though we use many different broken connections
|
|
|
|
|
within(5.seconds) {
|
2015-01-16 11:09:59 +01:00
|
|
|
receiveN(n) foreach { reply ⇒ reply should ===(("pong", testActor)) }
|
2013-01-25 15:03:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-12 11:18:42 +02:00
|
|
|
"create and supervise children on remote node" in {
|
2013-01-25 15:03:52 +01:00
|
|
|
val r = system.actorOf(Props[Echo1], "blub")
|
2015-01-16 11:09:59 +01:00
|
|
|
r.path.toString should ===("akka.test://remote-sys@localhost:12346/remote/akka.test/RemotingSpec@localhost:12345/user/blub")
|
2012-09-12 11:18:42 +02:00
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
EventFilter[Exception]("crash", occurrences = 1).intercept {
|
|
|
|
|
r ! new Exception("crash")
|
2013-03-07 12:10:30 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
expectMsg("preRestart")
|
|
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
system.stop(r)
|
|
|
|
|
expectMsg("postStop")
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-13 16:01:57 +01:00
|
|
|
"not send to remote re-created actor with same name" in {
|
2013-03-25 08:42:48 +01:00
|
|
|
val echo = remoteSystem.actorOf(Props[Echo1], "otherEcho1")
|
2013-03-13 16:01:57 +01:00
|
|
|
echo ! 71
|
|
|
|
|
expectMsg(71)
|
|
|
|
|
echo ! PoisonPill
|
|
|
|
|
expectMsg("postStop")
|
|
|
|
|
echo ! 72
|
|
|
|
|
expectNoMsg(1.second)
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
val echo2 = remoteSystem.actorOf(Props[Echo1], "otherEcho1")
|
2013-03-13 16:01:57 +01:00
|
|
|
echo2 ! 73
|
|
|
|
|
expectMsg(73)
|
|
|
|
|
// msg to old ActorRef (different uid) should not get through
|
2013-12-17 14:25:56 +01:00
|
|
|
echo2.path.uid should not be (echo.path.uid)
|
2013-03-13 16:01:57 +01:00
|
|
|
echo ! 74
|
|
|
|
|
expectNoMsg(1.second)
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
remoteSystem.actorFor("/user/otherEcho1") ! 75
|
2013-03-13 16:01:57 +01:00
|
|
|
expectMsg(75)
|
|
|
|
|
|
|
|
|
|
system.actorFor("akka.test://remote-sys@localhost:12346/user/otherEcho1") ! 76
|
|
|
|
|
expectMsg(76)
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
|
|
|
remoteSystem.actorSelection("/user/otherEcho1") ! 77
|
|
|
|
|
expectMsg(77)
|
|
|
|
|
|
|
|
|
|
system.actorSelection("akka.test://remote-sys@localhost:12346/user/otherEcho1") ! 78
|
|
|
|
|
expectMsg(78)
|
2013-03-13 16:01:57 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-12 11:18:42 +02:00
|
|
|
"look-up actors across node boundaries" in {
|
|
|
|
|
val l = system.actorOf(Props(new Actor {
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case (p: Props, n: String) ⇒ sender() ! context.actorOf(p, n)
|
|
|
|
|
case ActorForReq(s) ⇒ sender() ! context.actorFor(s)
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
2013-03-26 18:17:50 +01:00
|
|
|
}), "looker1")
|
2013-03-25 08:42:48 +01:00
|
|
|
// child is configured to be deployed on remote-sys (remoteSystem)
|
|
|
|
|
l ! ((Props[Echo1], "child"))
|
2013-03-13 16:01:57 +01:00
|
|
|
val child = expectMsgType[ActorRef]
|
|
|
|
|
// grandchild is configured to be deployed on RemotingSpec (system)
|
2013-03-25 08:42:48 +01:00
|
|
|
child ! ((Props[Echo1], "grandchild"))
|
2013-03-13 16:01:57 +01:00
|
|
|
val grandchild = expectMsgType[ActorRef]
|
2015-01-16 11:09:59 +01:00
|
|
|
grandchild.asInstanceOf[ActorRefScope].isLocal should ===(true)
|
2013-03-13 16:01:57 +01:00
|
|
|
grandchild ! 43
|
|
|
|
|
expectMsg(43)
|
2013-03-26 18:17:50 +01:00
|
|
|
val myref = system.actorFor(system / "looker1" / "child" / "grandchild")
|
2015-01-16 11:09:59 +01:00
|
|
|
myref.isInstanceOf[RemoteActorRef] should ===(true)
|
2013-03-13 16:01:57 +01:00
|
|
|
myref ! 44
|
|
|
|
|
expectMsg(44)
|
2015-01-16 11:09:59 +01:00
|
|
|
lastSender should ===(grandchild)
|
2013-12-17 14:25:56 +01:00
|
|
|
lastSender should be theSameInstanceAs grandchild
|
2015-01-16 11:09:59 +01:00
|
|
|
child.asInstanceOf[RemoteActorRef].getParent should ===(l)
|
2013-12-17 14:25:56 +01:00
|
|
|
system.actorFor("/user/looker1/child") should be theSameInstanceAs child
|
|
|
|
|
Await.result(l ? ActorForReq("child/.."), timeout.duration).asInstanceOf[AnyRef] should be theSameInstanceAs l
|
|
|
|
|
Await.result(system.actorFor(system / "looker1" / "child") ? ActorForReq(".."), timeout.duration).asInstanceOf[AnyRef] should be theSameInstanceAs l
|
2013-03-13 16:01:57 +01:00
|
|
|
|
|
|
|
|
watch(child)
|
|
|
|
|
child ! PoisonPill
|
|
|
|
|
expectMsg("postStop")
|
2013-04-09 14:48:17 +02:00
|
|
|
expectTerminated(child)
|
2013-03-25 08:42:48 +01:00
|
|
|
l ! ((Props[Echo1], "child"))
|
2013-03-13 16:01:57 +01:00
|
|
|
val child2 = expectMsgType[ActorRef]
|
|
|
|
|
child2 ! 45
|
|
|
|
|
expectMsg(45)
|
|
|
|
|
// msg to old ActorRef (different uid) should not get through
|
2013-12-17 14:25:56 +01:00
|
|
|
child2.path.uid should not be (child.path.uid)
|
2013-03-13 16:01:57 +01:00
|
|
|
child ! 46
|
|
|
|
|
expectNoMsg(1.second)
|
2013-03-26 18:17:50 +01:00
|
|
|
system.actorFor(system / "looker1" / "child") ! 47
|
2013-03-13 16:01:57 +01:00
|
|
|
expectMsg(47)
|
2013-03-26 18:17:50 +01:00
|
|
|
}
|
2013-03-13 16:01:57 +01:00
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
"select actors across node boundaries" in {
|
|
|
|
|
val l = system.actorOf(Props(new Actor {
|
|
|
|
|
def receive = {
|
2014-01-16 15:16:35 +01:00
|
|
|
case (p: Props, n: String) ⇒ sender() ! context.actorOf(p, n)
|
|
|
|
|
case ActorSelReq(s) ⇒ sender() ! context.actorSelection(s)
|
2013-03-26 18:17:50 +01:00
|
|
|
}
|
|
|
|
|
}), "looker2")
|
|
|
|
|
// child is configured to be deployed on remoteSystem
|
2013-05-29 16:13:10 +02:00
|
|
|
l ! ((Props[Echo1], "child"))
|
2013-03-26 18:17:50 +01:00
|
|
|
val child = expectMsgType[ActorRef]
|
|
|
|
|
// grandchild is configured to be deployed on RemotingSpec (system)
|
2013-05-29 16:13:10 +02:00
|
|
|
child ! ((Props[Echo1], "grandchild"))
|
2013-03-26 18:17:50 +01:00
|
|
|
val grandchild = expectMsgType[ActorRef]
|
2015-01-16 11:09:59 +01:00
|
|
|
grandchild.asInstanceOf[ActorRefScope].isLocal should ===(true)
|
2013-03-26 18:17:50 +01:00
|
|
|
grandchild ! 53
|
|
|
|
|
expectMsg(53)
|
|
|
|
|
val mysel = system.actorSelection(system / "looker2" / "child" / "grandchild")
|
|
|
|
|
mysel ! 54
|
|
|
|
|
expectMsg(54)
|
2015-01-16 11:09:59 +01:00
|
|
|
lastSender should ===(grandchild)
|
2013-12-17 14:25:56 +01:00
|
|
|
lastSender should be theSameInstanceAs grandchild
|
2013-03-26 18:17:50 +01:00
|
|
|
mysel ! Identify(mysel)
|
|
|
|
|
val grandchild2 = expectMsgType[ActorIdentity].ref
|
2015-01-16 11:09:59 +01:00
|
|
|
grandchild2 should ===(Some(grandchild))
|
2013-03-26 18:17:50 +01:00
|
|
|
system.actorSelection("/user/looker2/child") ! Identify(None)
|
2015-01-16 11:09:59 +01:00
|
|
|
expectMsgType[ActorIdentity].ref should ===(Some(child))
|
2013-03-26 18:17:50 +01:00
|
|
|
l ! ActorSelReq("child/..")
|
|
|
|
|
expectMsgType[ActorSelection] ! Identify(None)
|
2013-12-17 14:25:56 +01:00
|
|
|
expectMsgType[ActorIdentity].ref.get should be theSameInstanceAs l
|
2013-03-26 18:17:50 +01:00
|
|
|
system.actorSelection(system / "looker2" / "child") ! ActorSelReq("..")
|
|
|
|
|
expectMsgType[ActorSelection] ! Identify(None)
|
2013-12-17 14:25:56 +01:00
|
|
|
expectMsgType[ActorIdentity].ref.get should be theSameInstanceAs l
|
2013-03-26 18:17:50 +01:00
|
|
|
|
2013-10-25 16:17:51 +02:00
|
|
|
grandchild ! ((Props[Echo1], "grandgrandchild"))
|
|
|
|
|
val grandgrandchild = expectMsgType[ActorRef]
|
|
|
|
|
|
|
|
|
|
system.actorSelection("/user/looker2/child") ! Identify("idReq1")
|
2013-03-26 18:17:50 +01:00
|
|
|
expectMsg(ActorIdentity("idReq1", Some(child)))
|
2013-10-25 16:17:51 +02:00
|
|
|
system.actorSelection(child.path) ! Identify("idReq2")
|
|
|
|
|
expectMsg(ActorIdentity("idReq2", Some(child)))
|
|
|
|
|
system.actorSelection("/user/looker2/*") ! Identify("idReq3")
|
|
|
|
|
expectMsg(ActorIdentity("idReq3", Some(child)))
|
|
|
|
|
|
|
|
|
|
system.actorSelection("/user/looker2/child/grandchild") ! Identify("idReq4")
|
|
|
|
|
expectMsg(ActorIdentity("idReq4", Some(grandchild)))
|
|
|
|
|
system.actorSelection(child.path / "grandchild") ! Identify("idReq5")
|
|
|
|
|
expectMsg(ActorIdentity("idReq5", Some(grandchild)))
|
|
|
|
|
system.actorSelection("/user/looker2/*/grandchild") ! Identify("idReq6")
|
|
|
|
|
expectMsg(ActorIdentity("idReq6", Some(grandchild)))
|
|
|
|
|
system.actorSelection("/user/looker2/child/*") ! Identify("idReq7")
|
|
|
|
|
expectMsg(ActorIdentity("idReq7", Some(grandchild)))
|
|
|
|
|
system.actorSelection(child.path / "*") ! Identify("idReq8")
|
|
|
|
|
expectMsg(ActorIdentity("idReq8", Some(grandchild)))
|
|
|
|
|
|
|
|
|
|
system.actorSelection("/user/looker2/child/grandchild/grandgrandchild") ! Identify("idReq9")
|
|
|
|
|
expectMsg(ActorIdentity("idReq9", Some(grandgrandchild)))
|
|
|
|
|
system.actorSelection(child.path / "grandchild" / "grandgrandchild") ! Identify("idReq10")
|
|
|
|
|
expectMsg(ActorIdentity("idReq10", Some(grandgrandchild)))
|
|
|
|
|
system.actorSelection("/user/looker2/child/*/grandgrandchild") ! Identify("idReq11")
|
|
|
|
|
expectMsg(ActorIdentity("idReq11", Some(grandgrandchild)))
|
|
|
|
|
system.actorSelection("/user/looker2/child/*/*") ! Identify("idReq12")
|
|
|
|
|
expectMsg(ActorIdentity("idReq12", Some(grandgrandchild)))
|
|
|
|
|
system.actorSelection(child.path / "*" / "grandgrandchild") ! Identify("idReq13")
|
|
|
|
|
expectMsg(ActorIdentity("idReq13", Some(grandgrandchild)))
|
|
|
|
|
|
2014-02-03 09:37:43 -07:00
|
|
|
val sel1 = system.actorSelection("/user/looker2/child/grandchild/grandgrandchild")
|
|
|
|
|
system.actorSelection(sel1.toSerializationFormat) ! Identify("idReq18")
|
|
|
|
|
expectMsg(ActorIdentity("idReq18", Some(grandgrandchild)))
|
|
|
|
|
|
2013-10-25 16:17:51 +02:00
|
|
|
child ! Identify("idReq14")
|
|
|
|
|
expectMsg(ActorIdentity("idReq14", Some(child)))
|
2013-03-26 18:17:50 +01:00
|
|
|
watch(child)
|
|
|
|
|
child ! PoisonPill
|
|
|
|
|
expectMsg("postStop")
|
2015-01-16 11:09:59 +01:00
|
|
|
expectMsgType[Terminated].actor should ===(child)
|
2013-05-29 16:13:10 +02:00
|
|
|
l ! ((Props[Echo1], "child"))
|
2013-03-26 18:17:50 +01:00
|
|
|
val child2 = expectMsgType[ActorRef]
|
2013-10-25 16:17:51 +02:00
|
|
|
child2 ! Identify("idReq15")
|
|
|
|
|
expectMsg(ActorIdentity("idReq15", Some(child2)))
|
|
|
|
|
system.actorSelection(child.path) ! Identify("idReq16")
|
|
|
|
|
expectMsg(ActorIdentity("idReq16", Some(child2)))
|
|
|
|
|
child ! Identify("idReq17")
|
|
|
|
|
expectMsg(ActorIdentity("idReq17", None))
|
2013-03-26 18:17:50 +01:00
|
|
|
|
|
|
|
|
child2 ! 55
|
|
|
|
|
expectMsg(55)
|
|
|
|
|
// msg to old ActorRef (different uid) should not get through
|
2013-12-17 14:25:56 +01:00
|
|
|
child2.path.uid should not be (child.path.uid)
|
2013-03-26 18:17:50 +01:00
|
|
|
child ! 56
|
|
|
|
|
expectNoMsg(1.second)
|
|
|
|
|
system.actorSelection(system / "looker2" / "child") ! 57
|
|
|
|
|
expectMsg(57)
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-23 08:48:12 +02:00
|
|
|
"not fail ask across node boundaries" in within(5.seconds) {
|
2012-09-12 11:18:42 +02:00
|
|
|
import system.dispatcher
|
|
|
|
|
val f = for (_ ← 1 to 1000) yield here ? "ping" mapTo manifest[(String, ActorRef)]
|
2015-01-16 11:09:59 +01:00
|
|
|
Await.result(Future.sequence(f), timeout.duration).map(_._1).toSet should ===(Set("pong"))
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be able to use multiple transports and use the appropriate one (TCP)" in {
|
2013-01-25 15:03:52 +01:00
|
|
|
val r = system.actorOf(Props[Echo1], "gonk")
|
2016-08-03 14:06:57 +02:00
|
|
|
r.path.toString should ===(s"akka.tcp://remote-sys@localhost:${port(remoteSystem, "tcp")}/remote/akka.tcp/RemotingSpec@localhost:${port(system, "tcp")}/user/gonk")
|
2012-09-12 11:18:42 +02:00
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
EventFilter[Exception]("crash", occurrences = 1).intercept {
|
|
|
|
|
r ! new Exception("crash")
|
2013-03-07 12:10:30 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
expectMsg("preRestart")
|
|
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
system.stop(r)
|
|
|
|
|
expectMsg("postStop")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be able to use multiple transports and use the appropriate one (UDP)" in {
|
2013-01-25 15:03:52 +01:00
|
|
|
val r = system.actorOf(Props[Echo1], "zagzag")
|
2016-08-03 14:06:57 +02:00
|
|
|
r.path.toString should ===(s"akka.udp://remote-sys@localhost:${port(remoteSystem, "udp")}/remote/akka.udp/RemotingSpec@localhost:${port(system, "udp")}/user/zagzag")
|
2012-09-12 11:18:42 +02:00
|
|
|
r ! 42
|
2013-01-03 12:33:09 +01:00
|
|
|
expectMsg(10.seconds, 42)
|
2012-09-12 11:18:42 +02:00
|
|
|
EventFilter[Exception]("crash", occurrences = 1).intercept {
|
|
|
|
|
r ! new Exception("crash")
|
2013-03-07 12:10:30 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
expectMsg("preRestart")
|
|
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
system.stop(r)
|
|
|
|
|
expectMsg("postStop")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be able to use multiple transports and use the appropriate one (SSL)" in {
|
2013-01-25 15:03:52 +01:00
|
|
|
val r = system.actorOf(Props[Echo1], "roghtaar")
|
2016-08-03 14:06:57 +02:00
|
|
|
r.path.toString should ===(s"akka.ssl.tcp://remote-sys@localhost:${port(remoteSystem, "ssl.tcp")}/remote/akka.ssl.tcp/RemotingSpec@localhost:${port(system, "ssl.tcp")}/user/roghtaar")
|
2012-09-12 11:18:42 +02:00
|
|
|
r ! 42
|
2013-01-03 12:33:09 +01:00
|
|
|
expectMsg(10.seconds, 42)
|
2012-09-12 11:18:42 +02:00
|
|
|
EventFilter[Exception]("crash", occurrences = 1).intercept {
|
|
|
|
|
r ! new Exception("crash")
|
2013-03-07 12:10:30 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
expectMsg("preRestart")
|
|
|
|
|
r ! 42
|
|
|
|
|
expectMsg(42)
|
|
|
|
|
system.stop(r)
|
|
|
|
|
expectMsg("postStop")
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 20:38:49 +13:00
|
|
|
"drop unserializable messages" in {
|
|
|
|
|
object Unserializable
|
2013-07-01 12:51:15 +02:00
|
|
|
EventFilter[NotSerializableException](pattern = ".*No configured serialization.*", occurrences = 1).intercept {
|
2013-06-27 15:30:41 +02:00
|
|
|
verifySend(Unserializable) {
|
2013-07-01 12:51:15 +02:00
|
|
|
expectNoMsg(1.second) // No AssocitionErrorEvent should be published
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"allow messages up to payload size" in {
|
|
|
|
|
val maxProtocolOverhead = 500 // Make sure we're still under size after the message is serialized, etc
|
|
|
|
|
val big = byteStringOfSize(maxPayloadBytes - maxProtocolOverhead)
|
|
|
|
|
verifySend(big) {
|
2015-08-10 16:31:28 +02:00
|
|
|
expectMsg(3.seconds, big)
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"drop sent messages over payload size" in {
|
|
|
|
|
val oversized = byteStringOfSize(maxPayloadBytes + 1)
|
2013-07-01 12:51:15 +02:00
|
|
|
EventFilter[OversizedPayloadException](pattern = ".*Discarding oversized payload sent.*", occurrences = 1).intercept {
|
2013-06-27 15:30:41 +02:00
|
|
|
verifySend(oversized) {
|
2013-07-01 12:51:15 +02:00
|
|
|
expectNoMsg(1.second) // No AssocitionErrorEvent should be published
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"drop received messages over payload size" in {
|
|
|
|
|
// Receiver should reply with a message of size maxPayload + 1, which will be dropped and an error logged
|
2013-07-01 12:51:15 +02:00
|
|
|
EventFilter[OversizedPayloadException](pattern = ".*Discarding oversized payload received.*", occurrences = 1).intercept {
|
2013-06-27 15:30:41 +02:00
|
|
|
verifySend(maxPayloadBytes + 1) {
|
2013-07-01 12:51:15 +02:00
|
|
|
expectNoMsg(1.second) // No AssocitionErrorEvent should be published
|
2013-03-20 20:38:49 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 08:42:48 +01:00
|
|
|
"be able to serialize a local actor ref from another actor system" in {
|
|
|
|
|
val config = ConfigFactory.parseString("""
|
2015-02-17 13:40:48 +01:00
|
|
|
# Additional internal serialization verification need so be off, otherwise it triggers two error messages
|
|
|
|
|
# instead of one: one for the internal check, and one for the actual remote send -- tripping off this test
|
|
|
|
|
akka.actor.serialize-messages = off
|
|
|
|
|
akka.remote.enabled-transports = ["akka.remote.test", "akka.remote.netty.tcp"]
|
|
|
|
|
akka.remote.test.local-address = "test://other-system@localhost:12347"
|
|
|
|
|
""").withFallback(remoteSystem.settings.config)
|
2013-03-25 08:42:48 +01:00
|
|
|
val otherSystem = ActorSystem("other-system", config)
|
|
|
|
|
try {
|
|
|
|
|
val otherGuy = otherSystem.actorOf(Props[Echo2], "other-guy")
|
|
|
|
|
// check that we use the specified transport address instead of the default
|
|
|
|
|
val otherGuyRemoteTcp = otherGuy.path.toSerializationFormatWithAddress(addr(otherSystem, "tcp"))
|
|
|
|
|
val remoteEchoHereTcp = system.actorFor(s"akka.tcp://remote-sys@localhost:${port(remoteSystem, "tcp")}/user/echo")
|
2013-04-26 21:07:38 +02:00
|
|
|
val proxyTcp = system.actorOf(Props(classOf[Proxy], remoteEchoHereTcp, testActor), "proxy-tcp")
|
2013-03-25 08:42:48 +01:00
|
|
|
proxyTcp ! otherGuy
|
|
|
|
|
expectMsg(3.seconds, ("pong", otherGuyRemoteTcp))
|
|
|
|
|
// now check that we fall back to default when we haven't got a corresponding transport
|
|
|
|
|
val otherGuyRemoteTest = otherGuy.path.toSerializationFormatWithAddress(addr(otherSystem, "test"))
|
|
|
|
|
val remoteEchoHereSsl = system.actorFor(s"akka.ssl.tcp://remote-sys@localhost:${port(remoteSystem, "ssl.tcp")}/user/echo")
|
2013-04-26 21:07:38 +02:00
|
|
|
val proxySsl = system.actorOf(Props(classOf[Proxy], remoteEchoHereSsl, testActor), "proxy-ssl")
|
2016-05-27 16:45:48 +02:00
|
|
|
EventFilter.warning(start = "Error while resolving ActorRef", occurrences = 1).intercept {
|
2013-03-26 18:17:50 +01:00
|
|
|
proxySsl ! otherGuy
|
|
|
|
|
expectMsg(3.seconds, ("pong", otherGuyRemoteTest))
|
|
|
|
|
}(otherSystem)
|
2013-03-25 08:42:48 +01:00
|
|
|
} finally {
|
2013-05-02 17:12:36 +02:00
|
|
|
shutdown(otherSystem)
|
2013-03-25 08:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2014-04-02 14:48:32 +02:00
|
|
|
|
2015-02-17 13:40:48 +01:00
|
|
|
"should not publish AddressTerminated even on InvalidAssociationExecptions" in {
|
|
|
|
|
val localAddress = Address("akka.test", "system1", "localhost", 1)
|
|
|
|
|
val rawLocalAddress = localAddress.copy(protocol = "test")
|
|
|
|
|
val remoteAddress = Address("akka.test", "system2", "localhost", 2)
|
|
|
|
|
|
|
|
|
|
val config = ConfigFactory.parseString(s"""
|
|
|
|
|
akka.remote.enabled-transports = ["akka.remote.test"]
|
|
|
|
|
akka.remote.retry-gate-closed-for = 5s
|
|
|
|
|
|
|
|
|
|
akka.remote.test {
|
|
|
|
|
registry-key = tFdVxq
|
|
|
|
|
local-address = "test://${localAddress.system}@${localAddress.host.get}:${localAddress.port.get}"
|
|
|
|
|
}
|
|
|
|
|
""").withFallback(remoteSystem.settings.config)
|
|
|
|
|
|
|
|
|
|
val thisSystem = ActorSystem("this-system", config)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
class HackyRef extends MinimalActorRef {
|
|
|
|
|
@volatile var lastMsg: AnyRef = null
|
|
|
|
|
|
|
|
|
|
override def provider: ActorRefProvider = RARP(thisSystem).provider
|
|
|
|
|
|
|
|
|
|
override def path: ActorPath = thisSystem / "user" / "evilref"
|
|
|
|
|
|
|
|
|
|
override def isTerminated: Boolean = false
|
|
|
|
|
|
|
|
|
|
override def !(message: Any)(implicit sender: ActorRef): Unit = lastMsg = message.asInstanceOf[AnyRef]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val terminatedListener = new HackyRef
|
|
|
|
|
|
|
|
|
|
// Set up all connection attempts to fail
|
|
|
|
|
val registry = AssociationRegistry.get("tFdVxq")
|
|
|
|
|
awaitCond(registry.transportsReady(rawLocalAddress))
|
|
|
|
|
awaitCond {
|
|
|
|
|
registry.transportFor(rawLocalAddress) match {
|
|
|
|
|
case None ⇒ false
|
|
|
|
|
case Some((testTransport, _)) ⇒
|
|
|
|
|
testTransport.associateBehavior.pushError(new InvalidAssociationException("Test connection error"))
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddressTerminatedTopic(thisSystem).subscribe(terminatedListener)
|
|
|
|
|
|
|
|
|
|
val probe = new TestProbe(thisSystem)
|
|
|
|
|
val otherSelection = thisSystem.actorSelection(ActorPath.fromString(remoteAddress.toString + "/user/noonethere"))
|
|
|
|
|
otherSelection.tell("ping", probe.ref)
|
2015-08-18 15:29:41 +02:00
|
|
|
probe.expectNoMsg(1.second)
|
2015-02-17 13:40:48 +01:00
|
|
|
|
|
|
|
|
terminatedListener.lastMsg should be(null)
|
|
|
|
|
|
|
|
|
|
} finally shutdown(thisSystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"should stash inbound connections until UID is known for pending outbound" in {
|
|
|
|
|
val localAddress = Address("akka.test", "system1", "localhost", 1)
|
|
|
|
|
val rawLocalAddress = localAddress.copy(protocol = "test")
|
|
|
|
|
val remoteAddress = Address("akka.test", "system2", "localhost", 2)
|
|
|
|
|
val rawRemoteAddress = remoteAddress.copy(protocol = "test")
|
|
|
|
|
|
2014-04-02 14:48:32 +02:00
|
|
|
val config = ConfigFactory.parseString(s"""
|
2015-02-17 13:40:48 +01:00
|
|
|
akka.remote.enabled-transports = ["akka.remote.test"]
|
2014-04-02 14:48:32 +02:00
|
|
|
akka.remote.retry-gate-closed-for = 5s
|
2015-10-05 10:29:09 +02:00
|
|
|
akka.remote.log-remote-lifecycle-events = on
|
2015-02-17 13:40:48 +01:00
|
|
|
#akka.loglevel = DEBUG
|
|
|
|
|
|
|
|
|
|
akka.remote.test {
|
|
|
|
|
registry-key = TRKAzR
|
|
|
|
|
local-address = "test://${localAddress.system}@${localAddress.host.get}:${localAddress.port.get}"
|
|
|
|
|
}
|
2014-04-02 14:48:32 +02:00
|
|
|
""").withFallback(remoteSystem.settings.config)
|
|
|
|
|
val thisSystem = ActorSystem("this-system", config)
|
2015-02-17 13:40:48 +01:00
|
|
|
muteSystem(thisSystem)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// Set up a mock remote system using the test transport
|
|
|
|
|
val registry = AssociationRegistry.get("TRKAzR")
|
|
|
|
|
val remoteTransport = new TestTransport(rawRemoteAddress, registry)
|
|
|
|
|
val remoteTransportProbe = TestProbe()
|
|
|
|
|
|
|
|
|
|
registry.registerTransport(remoteTransport, associationEventListenerFuture = Future.successful(new Transport.AssociationEventListener {
|
|
|
|
|
override def notify(ev: Transport.AssociationEvent): Unit = remoteTransportProbe.ref ! ev
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
val outboundHandle = new TestAssociationHandle(rawLocalAddress, rawRemoteAddress, remoteTransport, inbound = false)
|
|
|
|
|
|
|
|
|
|
// Hijack associations through the test transport
|
|
|
|
|
awaitCond(registry.transportsReady(rawLocalAddress, rawRemoteAddress))
|
|
|
|
|
val testTransport = registry.transportFor(rawLocalAddress).get._1
|
|
|
|
|
testTransport.writeBehavior.pushConstant(true)
|
|
|
|
|
|
|
|
|
|
// Force an outbound associate on the real system (which we will hijack)
|
|
|
|
|
// we send no handshake packet, so this remains a pending connection
|
|
|
|
|
val dummySelection = thisSystem.actorSelection(ActorPath.fromString(remoteAddress.toString + "/user/noonethere"))
|
|
|
|
|
dummySelection.tell("ping", system.deadLetters)
|
|
|
|
|
|
|
|
|
|
val remoteHandle = remoteTransportProbe.expectMsgType[Transport.InboundAssociation]
|
|
|
|
|
remoteHandle.association.readHandlerPromise.success(new HandleEventListener {
|
|
|
|
|
override def notify(ev: HandleEvent): Unit = ()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Now we initiate an emulated inbound connection to the real system
|
|
|
|
|
val inboundHandleProbe = TestProbe()
|
|
|
|
|
val inboundHandle = Await.result(remoteTransport.associate(rawLocalAddress), 3.seconds)
|
|
|
|
|
inboundHandle.readHandlerPromise.success(new AssociationHandle.HandleEventListener {
|
|
|
|
|
override def notify(ev: HandleEvent): Unit = inboundHandleProbe.ref ! ev
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
registry.getRemoteReadHandlerFor(inboundHandle.asInstanceOf[TestAssociationHandle]).get
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val handshakePacket = AkkaPduProtobufCodec.constructAssociate(HandshakeInfo(rawRemoteAddress, uid = 0, cookie = None))
|
|
|
|
|
val brokenPacket = AkkaPduProtobufCodec.constructPayload(ByteString(0, 1, 2, 3, 4, 5, 6))
|
|
|
|
|
|
|
|
|
|
// Finish the inbound handshake so now it is handed up to Remoting
|
|
|
|
|
inboundHandle.write(handshakePacket)
|
|
|
|
|
// Now bork the connection with a malformed packet that can only signal an error if the Endpoint is already registered
|
|
|
|
|
// but not while it is stashed
|
|
|
|
|
inboundHandle.write(brokenPacket)
|
|
|
|
|
|
|
|
|
|
// No disassociation now, the connection is still stashed
|
|
|
|
|
inboundHandleProbe.expectNoMsg(1.second)
|
|
|
|
|
|
|
|
|
|
// Finish the handshake for the outbound connection. This will unstash the inbound pending connection.
|
|
|
|
|
remoteHandle.association.write(handshakePacket)
|
|
|
|
|
|
|
|
|
|
inboundHandleProbe.expectMsgType[AssociationHandle.Disassociated]
|
|
|
|
|
} finally shutdown(thisSystem)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 12:32:53 +02:00
|
|
|
"should properly quarantine stashed inbound connections" in {
|
|
|
|
|
val localAddress = Address("akka.test", "system1", "localhost", 1)
|
|
|
|
|
val rawLocalAddress = localAddress.copy(protocol = "test")
|
|
|
|
|
val remoteAddress = Address("akka.test", "system2", "localhost", 2)
|
|
|
|
|
val rawRemoteAddress = remoteAddress.copy(protocol = "test")
|
|
|
|
|
val remoteUID = 16
|
|
|
|
|
|
|
|
|
|
val config = ConfigFactory.parseString(s"""
|
|
|
|
|
akka.remote.enabled-transports = ["akka.remote.test"]
|
|
|
|
|
akka.remote.retry-gate-closed-for = 5s
|
2015-10-05 10:29:09 +02:00
|
|
|
akka.remote.log-remote-lifecycle-events = on
|
2015-08-26 12:32:53 +02:00
|
|
|
|
|
|
|
|
akka.remote.test {
|
|
|
|
|
registry-key = JMeMndLLsw
|
|
|
|
|
local-address = "test://${localAddress.system}@${localAddress.host.get}:${localAddress.port.get}"
|
|
|
|
|
}
|
|
|
|
|
""").withFallback(remoteSystem.settings.config)
|
|
|
|
|
val thisSystem = ActorSystem("this-system", config)
|
|
|
|
|
muteSystem(thisSystem)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// Set up a mock remote system using the test transport
|
|
|
|
|
val registry = AssociationRegistry.get("JMeMndLLsw")
|
|
|
|
|
val remoteTransport = new TestTransport(rawRemoteAddress, registry)
|
|
|
|
|
val remoteTransportProbe = TestProbe()
|
|
|
|
|
|
|
|
|
|
registry.registerTransport(remoteTransport, associationEventListenerFuture = Future.successful(new Transport.AssociationEventListener {
|
|
|
|
|
override def notify(ev: Transport.AssociationEvent): Unit = remoteTransportProbe.ref ! ev
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
val outboundHandle = new TestAssociationHandle(rawLocalAddress, rawRemoteAddress, remoteTransport, inbound = false)
|
|
|
|
|
|
|
|
|
|
// Hijack associations through the test transport
|
|
|
|
|
awaitCond(registry.transportsReady(rawLocalAddress, rawRemoteAddress))
|
|
|
|
|
val testTransport = registry.transportFor(rawLocalAddress).get._1
|
|
|
|
|
testTransport.writeBehavior.pushConstant(true)
|
|
|
|
|
|
|
|
|
|
// Force an outbound associate on the real system (which we will hijack)
|
|
|
|
|
// we send no handshake packet, so this remains a pending connection
|
|
|
|
|
val dummySelection = thisSystem.actorSelection(ActorPath.fromString(remoteAddress.toString + "/user/noonethere"))
|
|
|
|
|
dummySelection.tell("ping", system.deadLetters)
|
|
|
|
|
|
|
|
|
|
val remoteHandle = remoteTransportProbe.expectMsgType[Transport.InboundAssociation]
|
|
|
|
|
remoteHandle.association.readHandlerPromise.success(new HandleEventListener {
|
|
|
|
|
override def notify(ev: HandleEvent): Unit = ()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Now we initiate an emulated inbound connection to the real system
|
|
|
|
|
val inboundHandleProbe = TestProbe()
|
|
|
|
|
val inboundHandle = Await.result(remoteTransport.associate(rawLocalAddress), 3.seconds)
|
|
|
|
|
inboundHandle.readHandlerPromise.success(new AssociationHandle.HandleEventListener {
|
|
|
|
|
override def notify(ev: HandleEvent): Unit = inboundHandleProbe.ref ! ev
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
registry.getRemoteReadHandlerFor(inboundHandle.asInstanceOf[TestAssociationHandle]).get
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val handshakePacket = AkkaPduProtobufCodec.constructAssociate(HandshakeInfo(rawRemoteAddress, uid = remoteUID, cookie = None))
|
|
|
|
|
|
|
|
|
|
// Finish the inbound handshake so now it is handed up to Remoting
|
|
|
|
|
inboundHandle.write(handshakePacket)
|
|
|
|
|
|
|
|
|
|
// No disassociation now, the connection is still stashed
|
|
|
|
|
inboundHandleProbe.expectNoMsg(1.second)
|
|
|
|
|
|
|
|
|
|
// Quarantine unrelated connection
|
2016-09-07 16:07:29 +02:00
|
|
|
RARP(thisSystem).provider.quarantine(remoteAddress, Some(-1), "test")
|
2015-08-26 12:32:53 +02:00
|
|
|
inboundHandleProbe.expectNoMsg(1.second)
|
|
|
|
|
|
|
|
|
|
// Quarantine the connection
|
2016-09-26 15:34:59 +02:00
|
|
|
RARP(thisSystem).provider.quarantine(remoteAddress, Some(remoteUID.toLong), "test")
|
2015-08-26 12:32:53 +02:00
|
|
|
|
|
|
|
|
// Even though the connection is stashed it will be disassociated
|
|
|
|
|
inboundHandleProbe.expectMsgType[AssociationHandle.Disassociated]
|
|
|
|
|
|
|
|
|
|
} finally shutdown(thisSystem)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 13:40:48 +01:00
|
|
|
"be able to connect to system even if it's not there at first" in {
|
|
|
|
|
val config = ConfigFactory.parseString(s"""
|
|
|
|
|
akka.remote.enabled-transports = ["akka.remote.netty.tcp"]
|
|
|
|
|
akka.remote.netty.tcp.port = 0
|
|
|
|
|
akka.remote.retry-gate-closed-for = 5s
|
|
|
|
|
""").withFallback(remoteSystem.settings.config)
|
|
|
|
|
val thisSystem = ActorSystem("this-system", config)
|
2014-04-02 14:48:32 +02:00
|
|
|
try {
|
|
|
|
|
muteSystem(thisSystem)
|
|
|
|
|
val probe = new TestProbe(thisSystem)
|
|
|
|
|
val probeSender = probe.ref
|
|
|
|
|
val otherAddress = temporaryServerAddress()
|
|
|
|
|
val otherConfig = ConfigFactory.parseString(s"""
|
2015-02-17 13:40:48 +01:00
|
|
|
akka.remote.netty.tcp.port = ${otherAddress.getPort}
|
|
|
|
|
""").withFallback(config)
|
2014-04-02 14:48:32 +02:00
|
|
|
val otherSelection = thisSystem.actorSelection(s"akka.tcp://other-system@localhost:${otherAddress.getPort}/user/echo")
|
|
|
|
|
otherSelection.tell("ping", probeSender)
|
2015-01-30 16:34:27 +01:00
|
|
|
probe.expectNoMsg(1.seconds)
|
2014-04-02 14:48:32 +02:00
|
|
|
val otherSystem = ActorSystem("other-system", otherConfig)
|
|
|
|
|
try {
|
|
|
|
|
muteSystem(otherSystem)
|
2015-01-30 16:34:27 +01:00
|
|
|
probe.expectNoMsg(2.seconds)
|
2014-04-02 14:48:32 +02:00
|
|
|
otherSystem.actorOf(Props[Echo2], "echo")
|
2015-01-30 16:34:27 +01:00
|
|
|
within(5.seconds) {
|
2014-04-02 14:48:32 +02:00
|
|
|
awaitAssert {
|
|
|
|
|
otherSelection.tell("ping", probeSender)
|
2015-01-30 16:34:27 +01:00
|
|
|
assert(probe.expectMsgType[(String, ActorRef)](500.millis)._1 == "pong")
|
2014-04-02 14:48:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
shutdown(otherSystem)
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
shutdown(thisSystem)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"allow other system to connect even if it's not there at first" in {
|
|
|
|
|
val config = ConfigFactory.parseString(s"""
|
2015-02-17 13:40:48 +01:00
|
|
|
akka.remote.enabled-transports = ["akka.remote.netty.tcp"]
|
|
|
|
|
akka.remote.netty.tcp.port = 0
|
|
|
|
|
akka.remote.retry-gate-closed-for = 5s
|
|
|
|
|
""").withFallback(remoteSystem.settings.config)
|
2014-04-02 14:48:32 +02:00
|
|
|
val thisSystem = ActorSystem("this-system", config)
|
|
|
|
|
try {
|
|
|
|
|
muteSystem(thisSystem)
|
|
|
|
|
val thisProbe = new TestProbe(thisSystem)
|
|
|
|
|
val thisSender = thisProbe.ref
|
|
|
|
|
thisSystem.actorOf(Props[Echo2], "echo")
|
|
|
|
|
val otherAddress = temporaryServerAddress()
|
|
|
|
|
val otherConfig = ConfigFactory.parseString(s"""
|
2015-02-17 13:40:48 +01:00
|
|
|
akka.remote.netty.tcp.port = ${otherAddress.getPort}
|
|
|
|
|
""").withFallback(config)
|
2014-04-02 14:48:32 +02:00
|
|
|
val otherSelection = thisSystem.actorSelection(s"akka.tcp://other-system@localhost:${otherAddress.getPort}/user/echo")
|
|
|
|
|
otherSelection.tell("ping", thisSender)
|
2015-01-30 16:34:27 +01:00
|
|
|
thisProbe.expectNoMsg(1.seconds)
|
2014-04-02 14:48:32 +02:00
|
|
|
val otherSystem = ActorSystem("other-system", otherConfig)
|
|
|
|
|
try {
|
|
|
|
|
muteSystem(otherSystem)
|
2015-01-30 16:34:27 +01:00
|
|
|
thisProbe.expectNoMsg(2.seconds)
|
2014-04-02 14:48:32 +02:00
|
|
|
val otherProbe = new TestProbe(otherSystem)
|
|
|
|
|
val otherSender = otherProbe.ref
|
|
|
|
|
val thisSelection = otherSystem.actorSelection(s"akka.tcp://this-system@localhost:${port(thisSystem, "tcp")}/user/echo")
|
2015-01-30 16:34:27 +01:00
|
|
|
within(5.seconds) {
|
2014-04-02 14:48:32 +02:00
|
|
|
awaitAssert {
|
|
|
|
|
thisSelection.tell("ping", otherSender)
|
2015-01-30 16:34:27 +01:00
|
|
|
assert(otherProbe.expectMsgType[(String, ActorRef)](500.millis)._1 == "pong")
|
2014-04-02 14:48:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
shutdown(otherSystem)
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
shutdown(thisSystem)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-03 16:38:18 +01:00
|
|
|
}
|
2012-09-12 11:18:42 +02:00
|
|
|
}
|