Removing conflicting versions of typedActorOf and added Scala docs for TypedActor

This commit is contained in:
Viktor Klang 2011-12-14 23:43:21 +01:00
parent 0c44258050
commit 77e5596ad6
4 changed files with 228 additions and 35 deletions

View file

@ -98,7 +98,7 @@ object TypedActorSpec {
} }
def futureComposePigdogFrom(foo: Foo): Future[String] = { def futureComposePigdogFrom(foo: Foo): Future[String] = {
implicit val timeout = TypedActor.system.settings.ActorTimeout implicit val timeout = TypedActor.context.system.settings.ActorTimeout
foo.futurePigdog(500).map(_.toUpperCase) foo.futurePigdog(500).map(_.toUpperCase)
} }

View file

@ -229,7 +229,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
} }
private val selfReference = new ThreadLocal[AnyRef] private val selfReference = new ThreadLocal[AnyRef]
private val currentSystem = new ThreadLocal[ActorSystem] private val currentContext = new ThreadLocal[ActorContext]
/** /**
* Returns the reference to the proxy when called inside a method call in a TypedActor * Returns the reference to the proxy when called inside a method call in a TypedActor
@ -255,23 +255,30 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
} }
/** /**
* Returns the akka system (for a TypedActor) when inside a method call in a TypedActor. * Returns the ActorContext (for a TypedActor) when inside a method call in a TypedActor.
*/ */
def system = currentSystem.get match { def context = currentContext.get match {
case null throw new IllegalStateException("Calling TypedActor.system outside of a TypedActor implementation method!") case null throw new IllegalStateException("Calling TypedActor.context outside of a TypedActor implementation method!")
case some some case some some
} }
/** /**
* Returns the default dispatcher (for a TypedActor) when inside a method call in a TypedActor. * Returns the default dispatcher (for a TypedActor) when inside a method call in a TypedActor.
*/ */
implicit def dispatcher = system.dispatcher implicit def dispatcher = context.dispatcher
/** /**
* Implementation of TypedActor as an Actor * Implementation of TypedActor as an Actor
*/ */
private[akka] class TypedActor[R <: AnyRef, T <: R](val proxyVar: AtomVar[R], createInstance: T) extends Actor { private[akka] class TypedActor[R <: AnyRef, T <: R](val proxyVar: AtomVar[R], createInstance: T) extends Actor {
val me = createInstance val me = try {
TypedActor.selfReference set proxyVar.get
TypedActor.currentContext set context
createInstance
} finally {
TypedActor.selfReference set null
TypedActor.currentContext set null
}
override def preStart(): Unit = me match { override def preStart(): Unit = me match {
case l: PreStart l.preStart() case l: PreStart l.preStart()
@ -305,7 +312,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
def receive = { def receive = {
case m: MethodCall case m: MethodCall
TypedActor.selfReference set proxyVar.get TypedActor.selfReference set proxyVar.get
TypedActor.currentSystem set context.system TypedActor.currentContext set context
try { try {
if (m.isOneWay) m(me) if (m.isOneWay) m(me)
else { else {
@ -325,7 +332,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
} }
} finally { } finally {
TypedActor.selfReference set null TypedActor.selfReference set null
TypedActor.currentSystem set null TypedActor.currentContext set null
} }
} }
} }

View file

@ -1,9 +1,10 @@
package akka.docs.actor package akka.docs.actor
//#imports //#imports
import akka.actor.{ TypedActor, Props }
import akka.dispatch.{ Promise, Future, Await } import akka.dispatch.{ Promise, Future, Await }
import akka.util.duration._ import akka.util.duration._
import akka.actor.{ ActorContext, TypedActor, Props }
//#imports //#imports
import org.scalatest.{ BeforeAndAfterAll, WordSpec } import org.scalatest.{ BeforeAndAfterAll, WordSpec }
@ -12,6 +13,7 @@ import akka.testkit._
//#typed-actor-iface //#typed-actor-iface
trait Squarer { trait Squarer {
//#typed-actor-iface-methods
def squareDontCare(i: Int): Unit //fire-forget def squareDontCare(i: Int): Unit //fire-forget
def square(i: Int): Future[Int] //non-blocking send-request-reply def square(i: Int): Future[Int] //non-blocking send-request-reply
@ -19,11 +21,15 @@ trait Squarer {
def squareNowPlease(i: Int): Option[Int] //blocking send-request-reply def squareNowPlease(i: Int): Option[Int] //blocking send-request-reply
def squareNow(i: Int): Int //blocking send-request-reply def squareNow(i: Int): Int //blocking send-request-reply
//#typed-actor-iface-methods
} }
//#typed-actor-iface //#typed-actor-iface
//#typed-actor-impl //#typed-actor-impl
class SquarerImpl extends Squarer { class SquarerImpl(val name: String) extends Squarer {
def this() = this("default")
//#typed-actor-impl-methods
import TypedActor.dispatcher //So we can create Promises import TypedActor.dispatcher //So we can create Promises
def squareDontCare(i: Int): Unit = i * i //Nobody cares :( def squareDontCare(i: Int): Unit = i * i //Nobody cares :(
@ -33,25 +39,90 @@ class SquarerImpl extends Squarer {
def squareNowPlease(i: Int): Option[Int] = Some(i * i) def squareNowPlease(i: Int): Option[Int] = Some(i * i)
def squareNow(i: Int): Int = i * i def squareNow(i: Int): Int = i * i
//#typed-actor-impl-methods
} }
//#typed-actor-impl //#typed-actor-impl
//#typed-actor-supercharge
trait Foo {
def doFoo(times: Int): Unit = println("doFoo(" + times + ")")
}
trait Bar {
import TypedActor.dispatcher //So we have an implicit dispatcher for our Promise
def doBar(str: String): Future[String] = Promise successful str.toUpperCase
}
class FooBar extends Foo with Bar
//#typed-actor-supercharge
class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) { class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
"create a typed actor" in { "get the TypedActor extension" in {
//#typed-actor-create val someReference: AnyRef = null
val mySquarer = TypedActor(system).typedActorOf[Squarer, SquarerImpl]() try {
//#typed-actor-create //#typed-actor-extension-tools
import akka.actor.TypedActor
//Returns the Typed Actor Extension
val extension = TypedActor(system) //system is an instance of ActorSystem
//Returns whether the reference is a Typed Actor Proxy or not
TypedActor(system).isTypedActor(someReference)
//Returns the backing Akka Actor behind an external Typed Actor Proxy
TypedActor(system).getActorRefFor(someReference)
//Returns the current ActorContext,
// method only valid within methods of a TypedActor implementation
val c: ActorContext = TypedActor.context
//Returns the external proxy of the current Typed Actor,
// method only valid within methods of a TypedActor implementation
val s: Squarer = TypedActor.self[Squarer]
//Returns a contextual instance of the Typed Actor Extension
//this means that if you create other Typed Actors with this,
//they will become children to the current Typed Actor.
TypedActor(TypedActor.context)
//#typed-actor-extension-tools
} catch {
case e: Exception //dun care
}
}
"create a typed actor" in {
//#typed-actor-create1
val mySquarer: Squarer =
TypedActor(system).typedActorOf[Squarer, SquarerImpl]()
//#typed-actor-create1
//#typed-actor-create2
val otherSquarer: Squarer =
TypedActor(system).typedActorOf(classOf[Squarer],
new SquarerImpl("foo"),
Props(),
"name")
//#typed-actor-create2
//#typed-actor-calls //#typed-actor-calls
//#typed-actor-call-oneway
mySquarer.squareDontCare(10) mySquarer.squareDontCare(10)
//#typed-actor-call-oneway
//#typed-actor-call-future
val fSquare = mySquarer.square(10) //A Future[Int] val fSquare = mySquarer.square(10) //A Future[Int]
//#typed-actor-call-future
//#typed-actor-call-option
val oSquare = mySquarer.squareNowPlease(10) //Option[Int] val oSquare = mySquarer.squareNowPlease(10) //Option[Int]
//#typed-actor-call-option
//#typed-actor-call-strict
val iSquare = mySquarer.squareNow(10) //Int val iSquare = mySquarer.squareNow(10) //Int
//#typed-actor-call-strict
//#typed-actor-calls //#typed-actor-calls
Await.result(fSquare, 3 seconds) must be === 100 Await.result(fSquare, 3 seconds) must be === 100
@ -60,6 +131,24 @@ class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
iSquare must be === 100 iSquare must be === 100
//#typed-actor-stop
TypedActor(system).stop(mySquarer) TypedActor(system).stop(mySquarer)
//#typed-actor-stop
//#typed-actor-poisonpill
TypedActor(system).poisonPill(otherSquarer)
//#typed-actor-poisonpill
}
"supercharge" in {
//#typed-actor-supercharge-usage
val awesomeFooBar = TypedActor(system).typedActorOf[Foo with Bar, FooBar]()
awesomeFooBar.doFoo(10)
val f = awesomeFooBar.doBar("yes")
TypedActor(system).poisonPill(awesomeFooBar)
//#typed-actor-supercharge-usage
Await.result(f, 3 seconds) must be === "YES"
} }
} }

View file

@ -15,52 +15,149 @@ The advantage of Typed Actors vs. Actors is that with TypedActors you have a sta
Typed Actors are implemented using `JDK Proxies <http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html>`_ which provide a pretty easy-worked API to intercept method calls. Typed Actors are implemented using `JDK Proxies <http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html>`_ which provide a pretty easy-worked API to intercept method calls.
Typed Actor is an Akka Extension and could as such be implemented as a User Level API, but is provided in the core Akka package.
The tools of the trade
----------------------
Before we create our first Typed Actor we should first go through the tools that we have at our disposal,
it's located in ``akka.actor.TypedActor``.
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-extension-tools
.. warning::
Same as not exposing ``this`` of an Akka Actor, it's important not to expose ``this`` of a Typed Actor,
instead you should pass the external proxy reference, which is obtained from within your Typed Actor as
``TypedActor.self``, this is your external identity, as the ``ActorRef`` is the external identity of
and Akka Actor.
Creating Typed Actors
---------------------
To create a Typed Actor you need to have one or more interfaces, and one implementation.
Our example interface:
.. includecode:: code/TypedActorDocSpec.scala
:include: imports,typed-actor-iface
:exclude: typed-actor-iface-methods
Our example implementation of that interface:
.. includecode:: code/TypedActorDocSpec.scala
:include: imports,typed-actor-impl
:exclude: typed-actor-impl-methods
The most trivial way of creating a Typed Actor instance
of our Squarer:
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-create1
First type is the type of the proxy, the second type is the type of the implementation.
If you need to call a specific constructor you do it like this:
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-create2
Since you supply a Props, you can specify which dispatcher to use, what the default timeout should be used and more.
Now, our Squarer doesn't have any methods, so we'd better add those.
.. includecode:: code/TypedActorDocSpec.scala
:include: imports,typed-actor-iface
Alright, now we've got some methods we can call, but we need to implement those in SquarerImpl.
.. includecode:: code/TypedActorDocSpec.scala
:include: imports,typed-actor-impl
Alright, now we have an interface and an implementation of that interface,
and we know how to create a Typed Actor from that, so let's look at calling these methods.
Method dispatch semantics Method dispatch semantics
------------------------- -------------------------
Methods returning: Methods returning:
* ``Unit`` will be dispatched with ``fire-and-forget`` semantics, exactly like :meth:``Actor.tell`` * ``Unit`` will be dispatched with ``fire-and-forget`` semantics, exactly like ``Actor.tell``
* ``akka.dispatch.Future[_]`` will use ``send-request-reply`` semantics, exactly like :meth:``Actor.ask`` * ``akka.dispatch.Future[_]`` will use ``send-request-reply`` semantics, exactly like ``Actor.ask``
* ``scala.Option[_]`` or akka.japi.Option[_] will use ``send-request-reply`` semantics, but _will_ block to wait for an answer, * ``scala.Option[_]`` or akka.japi.Option[_] will use ``send-request-reply`` semantics, but _will_ block to wait for an answer,
and return None if no answer was produced within the timout, or scala.Some/akka.japi.Some containing the result otherwise. and return None if no answer was produced within the timout, or scala.Some/akka.japi.Some containing the result otherwise.
Any exception that was thrown during this call will be rethrown. Any exception that was thrown during this call will be rethrown.
* Any other type of value will use ``send-request-reply`` semantics, but _will_ block to wait for an answer, * Any other type of value will use ``send-request-reply`` semantics, but _will_ block to wait for an answer,
throwing ``java.util.concurrent.TimeoutException`` if there was a timeout or rethrow any exception that was thrown during this call. throwing ``java.util.concurrent.TimeoutException`` if there was a timeout or rethrow any exception that was thrown during this call.
Creating Typed Actors Messages and immutability
--------------------- -------------------------
To create a Typed Actor you need to have one or more interfaces, and one implementation: While Akka cannot enforce that the parameters to the methods of your Typed Actors are immutable,
we *strongly* recommend that parameters passed are immutable.
.. includecode:: code/TypedActorDocSpec.scala
:include: imports,typed-actor-create
If you need to call a specific constructor you do it like this:
INSERT EXAMPLE HERE
Since you supply a Props, you can specify which dispatcher to use etc.
Sending messages
----------------
One-way message send One-way message send
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-call-oneway
As simple as that! The method will be executed on another thread; asynchronously.
Request-reply message send Request-reply message send
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-call-option
This will block for as long as the timeout that was set in the Props of the Typed Actor,
if needed. It will return ``None`` if a timeout occurs.
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-call-strict
This will block for as long as the timeout that was set in the Props of the Typed Actor,
if needed. It will throw a ``java.util.concurrent.TimeoutException`` if a timeout occurs.
Request-reply-with-future message send Request-reply-with-future message send
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-call-future
This call is asynchronous, and the Future returned can be used for asynchronous composition.
Stopping Typed Actors Stopping Typed Actors
--------------------- ---------------------
Since Akkas Typed Actors are backed by Akka Actors they must be stopped when they aren't needed anymore.
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-stop
This asynchronously stops the Typed Actor associated with the specified proxy ASAP.
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-poisonpill
This asynchronously stops the Typed Actor associated with the specified proxy
after it's done with all calls that were made prior to this call.
Typed Actor Hierarchies
-----------------------
Since you can obtain a contextual Typed Actor Extension by passing in an ``ActorContext``
you can create child Typed Actors by invoking ``typedActorOf(..)`` on that.
This also works for creating child Typed Actors in regular Akka Actors.
Lifecycle callbacks Lifecycle callbacks
------------------- -------------------
Messages and immutability By having your Typed Actor implementation class implement ``TypedActor.PreStart``, ``TypedActor.PostStop``, ``TypedActor.PreRestart`` and/or ``TypedActor.PostRestart`` you can hook into the lifecyle of your Typed Actor.
-------------------------
Supercharging
-------------
Here's an example on how you can use traits to mix in behavior in your Typed Actors.
.. includecode:: code/TypedActorDocSpec.scala
:include: typed-actor-supercharge,typed-actor-supercharge-usage