-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
ActorRef.scala
487 lines (418 loc) · 16.2 KB
/
ActorRef.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.dispatch._
import akka.util._
import scala.collection.immutable.Stack
import java.lang.{ UnsupportedOperationException, IllegalStateException }
import akka.serialization.{ Serialization, JavaSerializer }
import akka.event.EventStream
import scala.annotation.tailrec
import java.util.concurrent.{ ConcurrentHashMap }
import akka.event.LoggingAdapter
/**
* Immutable and serializable handle to an actor, which may or may not reside
* on the local host or inside the same [[akka.actor.ActorSystem]]. An ActorRef
* can be obtained from an [[akka.actor.ActorRefFactory]], an interface which
* is implemented by ActorSystem and [[akka.actor.ActorContext]]. This means
* actors can be created top-level in the ActorSystem or as children of an
* existing actor, but only from within that actor.
*
* ActorRefs can be freely shared among actors by message passing. Message
* passing conversely is their only purpose, as demonstrated in the following
* examples:
*
* Scala:
* {{{
* import akka.pattern.ask
*
* class ExampleActor extends Actor {
* val other = context.actorOf(Props[OtherActor], "childName") // will be destroyed and re-created upon restart by default
*
* def receive {
* case Request1(msg) => other ! refine(msg) // uses this actor as sender reference, reply goes to us
* case Request2(msg) => other.tell(msg, sender) // forward sender reference, enabling direct reply
* case Request3(msg) => sender ! (other ? msg) // will reply with a Future for holding other’s reply (implicit timeout from "akka.actor.timeout")
* }
* }
* }}}
*
* Java:
* {{{
* import static akka.pattern.Patterns.ask;
*
* public class ExampleActor Extends UntypedActor {
* // this child will be destroyed and re-created upon restart by default
* final ActorRef other = getContext().actorOf(new Props(OtherActor.class), "childName");
*
* @Override
* public void onReceive(Object o) {
* if (o instanceof Request1) {
* val msg = ((Request1) o).getMsg();
* other.tell(msg); // uses this actor as sender reference, reply goes to us
*
* } else if (o instanceof Request2) {
* val msg = ((Request2) o).getMsg();
* other.tell(msg, getSender()); // forward sender reference, enabling direct reply
*
* } else if (o instanceof Request3) {
* val msg = ((Request3) o).getMsg();
* getSender().tell(ask(other, msg, 5000)); // reply with Future for holding the other’s reply (timeout 5 seconds)
*
* } else {
* unhandled(o);
* }
* }
* }
* }}}
*
* ActorRef does not have a method for terminating the actor it points to, use
* [[akka.actor.ActorRefFactory]]`.stop(child)` for this purpose.
*/
abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable {
scalaRef: InternalActorRef ⇒
/**
* Returns the path for this actor (from this actor up to the root actor).
*/
def path: ActorPath
/**
* Comparison only takes address into account.
*/
final def compareTo(other: ActorRef) = this.path compareTo other.path
/**
* Sends the specified message to the sender, i.e. fire-and-forget semantics.<p/>
* <pre>
* actor.tell(message);
* </pre>
*/
final def tell(msg: Any): Unit = this.!(msg)(null: ActorRef)
/**
* Java API. <p/>
* Sends the specified message to the sender, i.e. fire-and-forget
* semantics, including the sender reference if possible (not supported on
* all senders).<p/>
* <pre>
* actor.tell(message, context);
* </pre>
*/
final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)
/**
* Forwards the message and passes the original sender actor as the sender.
* <p/>
* Works with '!' and '?'/'ask'.
*/
def forward(message: Any)(implicit context: ActorContext) = tell(message, context.sender)
/**
* Is the actor shut down?
* The contract is that if this method returns true, then it will never be false again.
* But you cannot rely on that it is alive if it returns true, since this by nature is a racy method.
*/
def isTerminated: Boolean
// FIXME RK check if we should scramble the bits or whether they can stay the same
final override def hashCode: Int = path.hashCode
final override def equals(that: Any): Boolean = that match {
case other: ActorRef ⇒ path == other.path
case _ ⇒ false
}
override def toString = "Actor[%s]".format(path)
}
/**
* This trait represents the Scala Actor API
* There are implicit conversions in ../actor/Implicits.scala
* from ActorRef -> ScalaActorRef and back
*/
trait ScalaActorRef { ref: ActorRef ⇒
/**
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
* <p/>
*
* If invoked from within an actor then the actor reference is implicitly passed on as the implicit 'sender' argument.
* <p/>
*
* This actor 'sender' reference is then available in the receiving actor in the 'sender' member variable,
* if invoked from within an Actor. If not then no sender is available.
* <pre>
* actor ! message
* </pre>
* <p/>
*/
def !(message: Any)(implicit sender: ActorRef = null): Unit
}
/**
* All ActorRefs have a scope which describes where they live. Since it is
* often necessary to distinguish between local and non-local references, this
* is the only method provided on the scope.
*/
@deprecated("Will become private[akka] in 2.1, this is not user-api", "2.0.2")
trait ActorRefScope {
def isLocal: Boolean
}
@deprecated("Will become private[akka] in 2.1, this is not user-api", "2.0.2")
trait LocalRef extends ActorRefScope {
final def isLocal = true
}
/**
* Internal trait for assembling all the functionality needed internally on
* ActorRefs. NOTE THAT THIS IS NOT A STABLE EXTERNAL INTERFACE!
*
* DO NOT USE THIS UNLESS INTERNALLY WITHIN AKKA!
*/
private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRef { this: ActorRefScope ⇒
/*
* Actor life-cycle management, invoked only internally (in response to user requests via ActorContext).
*/
def resume(): Unit
def suspend(): Unit
def restart(cause: Throwable): Unit
def stop(): Unit
def sendSystemMessage(message: SystemMessage): Unit
/**
* Get a reference to the actor ref provider which created this ref.
*/
def provider: ActorRefProvider
/**
* Obtain parent of this ref; used by getChild for ".." paths.
*/
def getParent: InternalActorRef
/**
* Obtain ActorRef by possibly traversing the actor tree or looking it up at
* some provider-specific location. This method shall return the end result,
* i.e. not only the next step in the look-up; this will typically involve
* recursive invocation. A path element of ".." signifies the parent, a
* trailing "" element must be disregarded. If the requested path does not
* exist, return Nobody.
*/
def getChild(name: Iterator[String]): InternalActorRef
/**
* Scope: if this ref points to an actor which resides within the same JVM,
* i.e. whose mailbox is directly reachable etc.
*/
def isLocal: Boolean
}
/**
* This is an internal look-up failure token, not useful for anything else.
*/
private[akka] case object Nobody extends MinimalActorRef {
val path = new RootActorPath(Address("akka", "all-systems"), "/Nobody")
def provider = throw new UnsupportedOperationException("Nobody does not provide")
}
/**
* Local (serializable) ActorRef that is used when referencing the Actor on its "home" node.
*/
private[akka] class LocalActorRef private[akka] (
_system: ActorSystemImpl,
_props: Props,
_supervisor: InternalActorRef,
val path: ActorPath,
val systemService: Boolean = false,
_receiveTimeout: Option[Duration] = None)
extends InternalActorRef with LocalRef {
/*
* actorCell.start() publishes actorCell & this to the dispatcher, which
* means that messages may be processed theoretically before the constructor
* ends. The JMM guarantees visibility for final fields only after the end
* of the constructor, so publish the actorCell safely by making it a
* @volatile var which is NOT TO BE WRITTEN TO. The alternative would be to
* move start() outside of the constructor, which would basically require
* us to use purely factory methods for creating LocalActorRefs.
*/
@volatile
private var actorCell = newActorCell(_system, this, _props, _supervisor, _receiveTimeout)
actorCell.start()
protected def newActorCell(
system: ActorSystemImpl,
ref: InternalActorRef,
props: Props,
supervisor: InternalActorRef,
receiveTimeout: Option[Duration]): ActorCell =
new ActorCell(system, ref, props, supervisor, receiveTimeout)
protected def actorContext: ActorContext = actorCell
/**
* Is the actor terminated?
* If this method returns true, it will never return false again, but if it
* returns false, you cannot be sure if it's alive still (race condition)
*/
override def isTerminated: Boolean = actorCell.isTerminated
/**
* Suspends the actor so that it will not process messages until resumed. The
* suspend request is processed asynchronously to the caller of this method
* as well as to normal message sends: the only ordering guarantee is that
* message sends done from the same thread after calling this method will not
* be processed until resumed.
*/
def suspend(): Unit = actorCell.suspend()
/**
* Resumes a suspended actor.
*/
def resume(): Unit = actorCell.resume()
/**
* Shuts down the actor and its message queue
*/
def stop(): Unit = actorCell.stop()
def getParent: InternalActorRef = actorCell.parent
def provider = actorCell.provider
/**
* Method for looking up a single child beneath this actor. Override in order
* to inject “synthetic” actor paths like “/temp”.
*/
protected def getSingleChild(name: String): InternalActorRef =
actorCell.childrenRefs.getByName(name) match {
case Some(crs) ⇒ crs.child.asInstanceOf[InternalActorRef]
case None ⇒ Nobody
}
def getChild(names: Iterator[String]): InternalActorRef = {
/*
* The idea is to recursively descend as far as possible with LocalActor
* Refs and hand over to that “foreign” child when we encounter it.
*/
@tailrec
def rec(ref: InternalActorRef, name: Iterator[String]): InternalActorRef =
ref match {
case l: LocalActorRef ⇒
val n = name.next()
val next = n match {
case ".." ⇒ l.getParent
case "" ⇒ l
case _ ⇒ l.getSingleChild(n)
}
if (next == Nobody || name.isEmpty) next else rec(next, name)
case _ ⇒
ref.getChild(name)
}
if (names.isEmpty) this
else rec(this, names)
}
// ========= AKKA PROTECTED FUNCTIONS =========
protected[akka] def underlying: ActorCell = actorCell
def sendSystemMessage(message: SystemMessage) { underlying.dispatcher.systemDispatch(underlying, message) }
def !(message: Any)(implicit sender: ActorRef = null): Unit = actorCell.tell(message, sender)
def restart(cause: Throwable): Unit = actorCell.restart(cause)
@throws(classOf[java.io.ObjectStreamException])
protected def writeReplace(): AnyRef = SerializedActorRef(path)
}
/**
* Memento pattern for serializing ActorRefs transparently
*/
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
@deprecated("Will become private[akka] in 2.1, this is not user-api", "2.0.2")
case class SerializedActorRef private (path: String) {
import akka.serialization.JavaSerializer.currentSystem
@throws(classOf[java.io.ObjectStreamException])
def readResolve(): AnyRef = currentSystem.value match {
case null ⇒
throw new IllegalStateException(
"Trying to deserialize a serialized ActorRef without an ActorSystem in scope." +
" Use 'akka.serialization.Serialization.currentSystem.withValue(system) { ... }'")
case someSystem ⇒
someSystem.actorFor(path)
}
}
object SerializedActorRef {
def apply(path: ActorPath): SerializedActorRef = {
Serialization.currentTransportAddress.value match {
case null ⇒ new SerializedActorRef(path.toString)
case addr ⇒ new SerializedActorRef(path.toStringWithAddress(addr))
}
}
}
/**
* Trait for ActorRef implementations where all methods contain default stubs.
*/
private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
def getParent: InternalActorRef = Nobody
def getChild(names: Iterator[String]): InternalActorRef = {
val dropped = names.dropWhile(_.isEmpty)
if (dropped.isEmpty) this
else Nobody
}
def suspend(): Unit = ()
def resume(): Unit = ()
def stop(): Unit = ()
def isTerminated = false
def !(message: Any)(implicit sender: ActorRef = null): Unit = ()
def sendSystemMessage(message: SystemMessage): Unit = ()
def restart(cause: Throwable): Unit = ()
@throws(classOf[java.io.ObjectStreamException])
protected def writeReplace(): AnyRef = SerializedActorRef(path)
}
private[akka] object MinimalActorRef {
def apply(_path: ActorPath, _provider: ActorRefProvider)(receive: PartialFunction[Any, Unit]): ActorRef = new MinimalActorRef {
def path = _path
def provider = _provider
override def !(message: Any)(implicit sender: ActorRef = null): Unit =
if (receive.isDefinedAt(message)) receive(message)
}
}
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
private[akka] object DeadLetterActorRef {
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
class SerializedDeadLetterActorRef extends Serializable { //TODO implement as Protobuf for performance?
@throws(classOf[java.io.ObjectStreamException])
private def readResolve(): AnyRef = JavaSerializer.currentSystem.value.deadLetters
}
val serialized = new SerializedDeadLetterActorRef
}
/**
* This special dead letter reference has a name: it is that which is returned
* by a local look-up which is unsuccessful.
*/
private[akka] class EmptyLocalActorRef(
val provider: ActorRefProvider,
val path: ActorPath,
val eventStream: EventStream) extends MinimalActorRef {
override def isTerminated(): Boolean = true
override def !(message: Any)(implicit sender: ActorRef = null): Unit = message match {
case d: DeadLetter ⇒ // do NOT form endless loops, since deadLetters will resend!
case _ ⇒ eventStream.publish(DeadLetter(message, sender, this))
}
}
/**
* Internal implementation of the dead letter destination: will publish any
* received message to the eventStream, wrapped as [[akka.actor.DeadLetter]].
*/
private[akka] class DeadLetterActorRef(_provider: ActorRefProvider, _path: ActorPath, _eventStream: EventStream)
extends EmptyLocalActorRef(_provider, _path, _eventStream) {
override def !(message: Any)(implicit sender: ActorRef = this): Unit = message match {
case d: DeadLetter ⇒ eventStream.publish(d)
case _ ⇒ eventStream.publish(DeadLetter(message, sender, this))
}
@throws(classOf[java.io.ObjectStreamException])
override protected def writeReplace(): AnyRef = DeadLetterActorRef.serialized
}
/**
* Internal implementation detail used for paths like “/temp”
*/
private[akka] class VirtualPathContainer(
val provider: ActorRefProvider,
val path: ActorPath,
override val getParent: InternalActorRef,
val log: LoggingAdapter) extends MinimalActorRef {
private val children = new ConcurrentHashMap[String, InternalActorRef]
def addChild(name: String, ref: InternalActorRef): Unit = {
children.put(name, ref) match {
case null ⇒ // okay
case old ⇒ log.warning("{} replacing child {} ({} -> {})", path, name, old, ref)
}
}
def removeChild(name: String): Unit = {
children.remove(name) match {
case null ⇒ log.warning("{} trying to remove non-child {}", path, name)
case _ ⇒ //okay
}
}
def getChild(name: String): InternalActorRef = children.get(name)
override def getChild(name: Iterator[String]): InternalActorRef = {
if (name.isEmpty) this
else {
val n = name.next()
if (n.isEmpty) this
else children.get(n) match {
case null ⇒ Nobody
case some ⇒
if (name.isEmpty) some
else some.getChild(name)
}
}
}
}