2012-01-20 14:29:50 +01:00
|
|
|
/**
|
2013-01-09 01:47:48 +01:00
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
2012-01-20 14:29:50 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.remote
|
|
|
|
|
|
|
|
|
|
import scala.annotation.tailrec
|
2013-01-29 16:10:46 +01:00
|
|
|
import scala.util.control.NonFatal
|
2012-09-25 15:08:08 +02:00
|
|
|
import akka.actor.{ VirtualPathContainer, Terminated, Deploy, Props, Nobody, LocalActorRef, InternalActorRef, Address, ActorSystemImpl, ActorRef, ActorPathExtractor, ActorPath, Actor, AddressTerminated }
|
2012-01-20 14:29:50 +01:00
|
|
|
import akka.event.LoggingAdapter
|
2013-03-26 13:59:46 +01:00
|
|
|
import akka.dispatch.sysmsg.{ DeathWatchNotification, SystemMessage, Watch }
|
2012-06-13 17:57:56 +02:00
|
|
|
import akka.actor.ActorRefWithCell
|
|
|
|
|
import akka.actor.ActorRefScope
|
2012-09-25 18:35:07 +02:00
|
|
|
import akka.util.Switch
|
2013-03-13 16:01:57 +01:00
|
|
|
import akka.actor.RootActorPath
|
2013-03-26 18:17:50 +01:00
|
|
|
import akka.actor.SelectParent
|
|
|
|
|
import akka.actor.SelectChildName
|
|
|
|
|
import akka.actor.SelectChildPattern
|
|
|
|
|
import akka.actor.Identify
|
|
|
|
|
import akka.actor.ActorIdentity
|
2012-01-20 14:29:50 +01:00
|
|
|
|
2013-02-08 13:13:52 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
2012-02-03 09:43:23 +01:00
|
|
|
private[akka] sealed trait DaemonMsg
|
2013-02-08 13:13:52 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
@SerialVersionUID(1L)
|
2012-02-03 09:43:23 +01:00
|
|
|
private[akka] case class DaemonMsgCreate(props: Props, deploy: Deploy, path: String, supervisor: ActorRef) extends DaemonMsg
|
2012-01-20 14:29:50 +01:00
|
|
|
|
|
|
|
|
/**
|
2013-02-08 13:13:52 +01:00
|
|
|
* INTERNAL API
|
|
|
|
|
*
|
2012-01-20 14:29:50 +01:00
|
|
|
* Internal system "daemon" actor for remote internal communication.
|
|
|
|
|
*
|
|
|
|
|
* It acts as the brain of the remote that responds to system remote events (messages) and undertakes action.
|
|
|
|
|
*/
|
2012-10-04 16:50:49 -07:00
|
|
|
private[akka] class RemoteSystemDaemon(
|
|
|
|
|
system: ActorSystemImpl,
|
|
|
|
|
_path: ActorPath,
|
|
|
|
|
_parent: InternalActorRef,
|
2012-12-14 16:09:38 +01:00
|
|
|
terminator: ActorRef,
|
2012-10-04 16:50:49 -07:00
|
|
|
_log: LoggingAdapter,
|
|
|
|
|
val untrustedMode: Boolean)
|
2012-01-26 11:24:23 +01:00
|
|
|
extends VirtualPathContainer(system.provider, _path, _parent, _log) {
|
2012-01-20 14:29:50 +01:00
|
|
|
|
2012-09-28 12:24:17 +02:00
|
|
|
import akka.actor.SystemGuardian._
|
2012-09-25 14:12:04 +02:00
|
|
|
|
2012-09-27 08:12:48 +02:00
|
|
|
private val terminating = new Switch(false)
|
2012-09-25 15:08:08 +02:00
|
|
|
|
|
|
|
|
system.eventStream.subscribe(this, classOf[AddressTerminated])
|
2012-09-25 14:12:04 +02:00
|
|
|
|
2012-01-20 14:29:50 +01:00
|
|
|
/**
|
|
|
|
|
* Find the longest matching path which we know about and return that ref
|
|
|
|
|
* (or ask that ref to continue searching if elements are left).
|
|
|
|
|
*/
|
|
|
|
|
override def getChild(names: Iterator[String]): InternalActorRef = {
|
|
|
|
|
|
|
|
|
|
@tailrec
|
|
|
|
|
def rec(s: String, n: Int): (InternalActorRef, Int) = {
|
2013-03-13 16:01:57 +01:00
|
|
|
import akka.actor.ActorCell._
|
|
|
|
|
val (childName, uid) = splitNameAndUid(s)
|
|
|
|
|
getChild(childName) match {
|
2012-01-20 14:29:50 +01:00
|
|
|
case null ⇒
|
|
|
|
|
val last = s.lastIndexOf('/')
|
|
|
|
|
if (last == -1) (Nobody, n)
|
|
|
|
|
else rec(s.substring(0, last), n + 1)
|
2013-03-13 16:01:57 +01:00
|
|
|
case ref if uid != undefinedUid && uid != ref.path.uid ⇒ (Nobody, n)
|
2012-01-20 14:29:50 +01:00
|
|
|
case ref ⇒ (ref, n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val full = Vector() ++ names
|
|
|
|
|
rec(full.mkString("/"), 0) match {
|
|
|
|
|
case (Nobody, _) ⇒ Nobody
|
|
|
|
|
case (ref, 0) ⇒ ref
|
|
|
|
|
case (ref, n) ⇒ ref.getChild(full.takeRight(n).iterator)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-26 13:59:46 +01:00
|
|
|
override def sendSystemMessage(message: SystemMessage): Unit = message match {
|
|
|
|
|
case DeathWatchNotification(child: ActorRefWithCell with ActorRefScope, _, _) if child.isLocal ⇒
|
|
|
|
|
terminating.locked {
|
2013-05-31 14:03:18 +02:00
|
|
|
removeChild(child.path.elements.drop(1).mkString("/"), child)
|
2013-03-26 13:59:46 +01:00
|
|
|
terminationHookDoneWhenNoChildren()
|
|
|
|
|
}
|
|
|
|
|
case _ ⇒ super.sendSystemMessage(message)
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-29 16:10:46 +01:00
|
|
|
override def !(msg: Any)(implicit sender: ActorRef = Actor.noSender): Unit = try msg match {
|
2012-09-25 18:35:07 +02:00
|
|
|
case message: DaemonMsg ⇒
|
2012-01-20 14:29:50 +01:00
|
|
|
log.debug("Received command [{}] to RemoteSystemDaemon on [{}]", message, path.address)
|
|
|
|
|
message match {
|
2013-01-29 16:10:46 +01:00
|
|
|
case DaemonMsgCreate(_, _, path, _) if untrustedMode ⇒ log.debug("does not accept deployments (untrusted) for [{}]", path)
|
2012-01-31 21:19:28 +01:00
|
|
|
case DaemonMsgCreate(props, deploy, path, supervisor) ⇒
|
2012-01-20 14:29:50 +01:00
|
|
|
path match {
|
2012-01-27 12:14:28 +01:00
|
|
|
case ActorPathExtractor(address, elems) if elems.nonEmpty && elems.head == "remote" ⇒
|
|
|
|
|
// TODO RK currently the extracted “address” is just ignored, is that okay?
|
2012-01-20 14:29:50 +01:00
|
|
|
// TODO RK canonicalize path so as not to duplicate it always #1446
|
|
|
|
|
val subpath = elems.drop(1)
|
2013-03-13 16:01:57 +01:00
|
|
|
val p = this.path / subpath
|
|
|
|
|
val childName = {
|
|
|
|
|
val s = subpath.mkString("/")
|
|
|
|
|
val i = s.indexOf('#')
|
|
|
|
|
if (i < 0) s
|
|
|
|
|
else s.substring(0, i)
|
|
|
|
|
}
|
2012-09-27 08:35:01 +02:00
|
|
|
val isTerminating = !terminating.whileOff {
|
|
|
|
|
val actor = system.provider.actorOf(system, props, supervisor.asInstanceOf[InternalActorRef],
|
2013-03-13 16:01:57 +01:00
|
|
|
p, systemService = false, Some(deploy), lookupDeploy = true, async = false)
|
|
|
|
|
addChild(childName, actor)
|
2012-09-27 08:35:01 +02:00
|
|
|
actor.sendSystemMessage(Watch(actor, this))
|
2013-01-25 08:04:01 +01:00
|
|
|
actor.start()
|
2012-09-27 08:35:01 +02:00
|
|
|
}
|
2013-03-13 16:01:57 +01:00
|
|
|
if (isTerminating) log.error("Skipping [{}] to RemoteSystemDaemon on [{}] while terminating", message, p.address)
|
2012-01-20 14:29:50 +01:00
|
|
|
case _ ⇒
|
2012-10-04 16:50:49 -07:00
|
|
|
log.debug("remote path does not match path from message [{}]", message)
|
2012-01-20 14:29:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
case SelectParent(m) ⇒ getParent.tell(m, sender)
|
|
|
|
|
|
|
|
|
|
case s @ SelectChildName(name, m) ⇒
|
|
|
|
|
getChild(s.allChildNames.iterator) match {
|
|
|
|
|
case Nobody ⇒
|
|
|
|
|
s.identifyRequest foreach { x ⇒ sender ! ActorIdentity(x.messageId, None) }
|
|
|
|
|
case child ⇒
|
|
|
|
|
child.tell(s.wrappedMessage, sender)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SelectChildPattern(p, m) ⇒
|
|
|
|
|
log.error("SelectChildPattern not allowed in actorSelection of remote deployed actors")
|
|
|
|
|
|
|
|
|
|
case Identify(messageId) ⇒ sender ! ActorIdentity(messageId, Some(this))
|
|
|
|
|
|
2012-09-25 14:12:04 +02:00
|
|
|
case TerminationHook ⇒
|
2012-09-25 18:35:07 +02:00
|
|
|
terminating.switchOn {
|
|
|
|
|
terminationHookDoneWhenNoChildren()
|
2012-09-27 14:28:40 +02:00
|
|
|
foreachChild { system.stop }
|
2012-09-25 18:35:07 +02:00
|
|
|
}
|
2012-09-25 14:12:04 +02:00
|
|
|
|
2012-09-25 15:08:08 +02:00
|
|
|
case AddressTerminated(address) ⇒
|
2012-10-26 16:56:23 +02:00
|
|
|
foreachChild {
|
|
|
|
|
case a: InternalActorRef if a.getParent.path.address == address ⇒ system.stop(a)
|
|
|
|
|
case _ ⇒ // skip, this child doesn't belong to the terminated address
|
|
|
|
|
}
|
2012-09-25 15:08:08 +02:00
|
|
|
|
2013-01-29 16:10:46 +01:00
|
|
|
case unknown ⇒ log.warning("Unknown message [{}] received by [{}]", unknown, this)
|
|
|
|
|
|
|
|
|
|
} catch {
|
|
|
|
|
case NonFatal(e) ⇒ log.error(e, "exception while processing remote command [{}] from [{}]", msg, sender)
|
2012-01-20 14:29:50 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-27 14:28:40 +02:00
|
|
|
def terminationHookDoneWhenNoChildren(): Unit = terminating.whileOn {
|
2012-12-14 16:09:38 +01:00
|
|
|
if (!hasChildren) terminator.tell(TerminationHookDone, this)
|
2012-09-27 14:28:40 +02:00
|
|
|
}
|
2012-09-25 14:12:04 +02:00
|
|
|
|
2012-01-20 14:29:50 +01:00
|
|
|
}
|