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] = {
implicit val timeout = TypedActor.system.settings.ActorTimeout
implicit val timeout = TypedActor.context.system.settings.ActorTimeout
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 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
@ -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 {
case null throw new IllegalStateException("Calling TypedActor.system outside of a TypedActor implementation method!")
def context = currentContext.get match {
case null throw new IllegalStateException("Calling TypedActor.context outside of a TypedActor implementation method!")
case some some
}
/**
* 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
*/
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 {
case l: PreStart l.preStart()
@ -305,7 +312,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
def receive = {
case m: MethodCall
TypedActor.selfReference set proxyVar.get
TypedActor.currentSystem set context.system
TypedActor.currentContext set context
try {
if (m.isOneWay) m(me)
else {
@ -325,7 +332,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
}
} finally {
TypedActor.selfReference set null
TypedActor.currentSystem set null
TypedActor.currentContext set null
}
}
}

View file

@ -1,9 +1,10 @@
package akka.docs.actor
//#imports
import akka.actor.{ TypedActor, Props }
import akka.dispatch.{ Promise, Future, Await }
import akka.util.duration._
import akka.actor.{ ActorContext, TypedActor, Props }
//#imports
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
@ -12,6 +13,7 @@ import akka.testkit._
//#typed-actor-iface
trait Squarer {
//#typed-actor-iface-methods
def squareDontCare(i: Int): Unit //fire-forget
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 squareNow(i: Int): Int //blocking send-request-reply
//#typed-actor-iface-methods
}
//#typed-actor-iface
//#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
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 squareNow(i: Int): Int = i * i
//#typed-actor-impl-methods
}
//#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")) {
"create a typed actor" in {
//#typed-actor-create
"get the TypedActor extension" in {
val someReference: AnyRef = null
val mySquarer = TypedActor(system).typedActorOf[Squarer, SquarerImpl]()
//#typed-actor-create
try {
//#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-call-oneway
mySquarer.squareDontCare(10)
//#typed-actor-call-oneway
//#typed-actor-call-future
val fSquare = mySquarer.square(10) //A Future[Int]
//#typed-actor-call-future
//#typed-actor-call-option
val oSquare = mySquarer.squareNowPlease(10) //Option[Int]
//#typed-actor-call-option
//#typed-actor-call-strict
val iSquare = mySquarer.squareNow(10) //Int
//#typed-actor-call-strict
//#typed-actor-calls
Await.result(fSquare, 3 seconds) must be === 100
@ -60,6 +131,24 @@ class TypedActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
iSquare must be === 100
//#typed-actor-stop
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 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
-------------------------
Methods returning:
* ``Unit`` will be dispatched with ``fire-and-forget`` semantics, exactly like :meth:``Actor.tell``
* ``akka.dispatch.Future[_]`` will use ``send-request-reply`` semantics, exactly like :meth:``Actor.ask``
* ``Unit`` will be dispatched with ``fire-and-forget`` semantics, exactly like ``Actor.tell``
* ``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,
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 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.
Creating Typed Actors
---------------------
Messages and immutability
-------------------------
To create a Typed Actor you need to have one or more interfaces, and one implementation:
.. 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
----------------
While Akka cannot enforce that the parameters to the methods of your Typed Actors are immutable,
we *strongly* recommend that parameters passed are immutable.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. 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
---------------------
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
-------------------
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