Adding Typed Actor docs for Java, as well as some minor tweaks on some Java APIs

This commit is contained in:
Viktor Klang 2011-12-15 16:56:53 +01:00
parent a5613729fa
commit 5e03cda4bb
14 changed files with 369 additions and 19 deletions

View file

@ -146,6 +146,8 @@ trait TypedActorFactory {
}
object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvider {
override def get(system: ActorSystem): TypedActorExtension = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl): TypedActorExtension = new TypedActorExtension(system)

View file

@ -45,6 +45,9 @@ object Await {
def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable.result(atMost)
}
/**
* Futures is the Java API for Futures and Promises
*/
object Futures {
/**
@ -57,6 +60,16 @@ object Futures {
*/
def promise[T](dispatcher: MessageDispatcher): Promise[T] = Promise[T]()(dispatcher)
/**
* Java API, creates an already completed Promise with the specified exception
*/
def failed[T](exception: Throwable, dispatcher: MessageDispatcher): Promise[T] = Promise.failed(exception)(dispatcher)
/**
* Java API, Creates an already completed Promise with the specified result
*/
def successful[T](result: T, dispatcher: MessageDispatcher): Promise[T] = Promise.successful(result)(dispatcher)
/**
* Java API.
* Returns a Future that will hold the optional result of the first Future with a result that matches the predicate

View file

@ -3,9 +3,10 @@
*/
package akka.serialization
import akka.actor.{ ExtensionId, ExtensionIdProvider, ActorSystemImpl }
import akka.actor.{ ActorSystem, ExtensionId, ExtensionIdProvider, ActorSystemImpl }
object SerializationExtension extends ExtensionId[Serialization] with ExtensionIdProvider {
override def get(system: ActorSystem): Serialization = super.get(system)
override def lookup = SerializationExtension
override def createExtension(system: ActorSystemImpl): Serialization = new Serialization(system)
}

View file

@ -0,0 +1,5 @@
package akka.docs.actor
import org.scalatest.junit.JUnitSuite
class TypedActorDocTest extends TypedActorDocTestBase with JUnitSuite

View file

@ -0,0 +1,150 @@
package akka.docs.actor;
//#imports
import akka.dispatch.*;
import akka.actor.*;
import akka.japi.*;
import akka.util.Duration;
import java.util.concurrent.TimeUnit;
//#imports
import java.lang.Exception;
import org.junit.Test;
import static org.junit.Assert.*;
public class TypedActorDocTestBase {
Object someReference = null;
ActorSystem system = null;
//#typed-actor-iface
public static interface Squarer {
//#typed-actor-iface-methods
void squareDontCare(int i); //fire-forget
Future<Integer> square(int i); //non-blocking send-request-reply
Option<Integer> squareNowPlease(int i);//blocking send-request-reply
int squareNow(int i); //blocking send-request-reply
//#typed-actor-iface-methods
}
//#typed-actor-iface
//#typed-actor-impl
static class SquarerImpl implements Squarer {
private String name;
public SquarerImpl() {
this.name = "default";
}
public SquarerImpl(String name) {
this.name = name;
}
//#typed-actor-impl-methods
public void squareDontCare(int i) {
int sq = i * i; //Nobody cares :(
}
public Future<Integer> square(int i) {
return Futures.successful(i * i, TypedActor.dispatcher());
}
public Option<Integer> squareNowPlease(int i) {
return Option.some(i * i);
}
public int squareNow(int i) {
return i * i;
}
//#typed-actor-impl-methods
}
//#typed-actor-impl
@Test public void mustGetTheTypedActorExtension() {
try {
//#typed-actor-extension-tools
//Returns the Typed Actor Extension
TypedActorExtension extension =
TypedActor.get(system); //system is an instance of ActorSystem
//Returns whether the reference is a Typed Actor Proxy or not
TypedActor.get(system).isTypedActor(someReference);
//Returns the backing Akka Actor behind an external Typed Actor Proxy
TypedActor.get(system).getActorRefFor(someReference);
//Returns the current ActorContext,
// method only valid within methods of a TypedActor implementation
ActorContext context = TypedActor.context();
//Returns the external proxy of the current Typed Actor,
// method only valid within methods of a TypedActor implementation
Squarer sq = TypedActor.<Squarer>self();
//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.get(TypedActor.context());
//#typed-actor-extension-tools
} catch (Exception e) {
//dun care
}
}
@Test public void createATypedActor() {
try {
//#typed-actor-create1
Squarer mySquarer =
TypedActor.get(system).typedActorOf(Squarer.class, SquarerImpl.class, new Props());
//#typed-actor-create1
//#typed-actor-create2
Squarer otherSquarer =
TypedActor.get(system).typedActorOf(Squarer.class,
new Creator<SquarerImpl>() {
public SquarerImpl create() { return new SquarerImpl("foo"); }
},
new Props(),
"name");
//#typed-actor-create2
//#typed-actor-calls
//#typed-actor-call-oneway
mySquarer.squareDontCare(10);
//#typed-actor-call-oneway
//#typed-actor-call-future
Future<Integer> fSquare = mySquarer.square(10); //A Future[Int]
//#typed-actor-call-future
//#typed-actor-call-option
Option<Integer> oSquare = mySquarer.squareNowPlease(10); //Option[Int]
//#typed-actor-call-option
//#typed-actor-call-strict
int iSquare = mySquarer.squareNow(10); //Int
//#typed-actor-call-strict
//#typed-actor-calls
assertEquals(100, Await.result(fSquare, Duration.create(3, TimeUnit.SECONDS)).intValue());
assertEquals(100, oSquare.get().intValue());
assertEquals(100, iSquare);
//#typed-actor-stop
TypedActor.get(system).stop(mySquarer);
//#typed-actor-stop
//#typed-actor-poisonpill
TypedActor.get(system).poisonPill(otherSquarer);
//#typed-actor-poisonpill
} catch(Exception e) {
//Ignore
}
}
}

View file

@ -0,0 +1,166 @@
Typed Actors (Scala)
====================
.. sidebar:: Contents
.. contents:: :local:
Akka Typed Actors is an implementation of the `Active Objects <http://en.wikipedia.org/wiki/Active_object>`_ pattern.
Essentially turning method invocations into asynchronous dispatch instead of synchronous that has been the default way since Smalltalk came out.
Typed Actors consist of 2 "parts", a public interface and an implementation, and if you've done any work in "enterprise" Java, this will be very familiar to you. As with normal Actors you have an external API (the public interface instance) that will delegate methodcalls asynchronously to
a private instance of the implementation.
The advantage of Typed Actors vs. Actors is that with TypedActors you have a static contract, and don't need to define your own messages, the downside is that it places some limitations on what you can do and what you can't, i.e. you can't use become/unbecome.
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.
.. note::
Just as with regular Akka Untyped Actors, Typed Actors process one call at a time.
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/akka/docs/actor/TypedActorDocTestBase.java
: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
an 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/akka/docs/actor/TypedActorDocTestBase.java
:include: imports,typed-actor-iface
:exclude: typed-actor-iface-methods
Our example implementation of that interface:
.. includecode:: code/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
:include: imports,typed-actor-impl
Excellent, 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 ``ActorRef.tell``
* ``akka.dispatch.Future<?>`` will use ``send-request-reply`` semantics, exactly like ``ActorRef.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.
Messages and immutability
-------------------------
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/akka/docs/actor/TypedActorDocTestBase.java
:include: typed-actor-call-oneway
As simple as that! The method will be executed on another thread; asynchronously.
Request-reply message send
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
: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/akka/docs/actor/TypedActorDocTestBase.java
:include: typed-actor-stop
This asynchronously stops the Typed Actor associated with the specified proxy ASAP.
.. includecode:: code/akka/docs/actor/TypedActorDocTestBase.java
: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
-------------------
By having your Typed Actor implementation class implement any and all of the following:
* ``TypedActor.PreStart``
* ``TypedActor.PostStop``
* ``TypedActor.PreRestart``
* ``TypedActor.PostRestart``
You can hook into the lifecycle of your Typed Actor.

View file

@ -26,7 +26,7 @@ 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
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: typed-actor-extension-tools
.. warning::
@ -43,37 +43,37 @@ To create a Typed Actor you need to have one or more interfaces, and one impleme
Our example interface:
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: imports,typed-actor-iface
:exclude: typed-actor-iface-methods
Our example implementation of that interface:
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/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
.. includecode:: code/akka/docs/actor/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
.. includecode:: code/akka/docs/actor/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
.. includecode:: code/akka/docs/actor/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
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: imports,typed-actor-impl
Excellent, now we have an interface and an implementation of that interface,
@ -84,8 +84,8 @@ Method dispatch semantics
Methods returning:
* ``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``
* ``Unit`` will be dispatched with ``fire-and-forget`` semantics, exactly like ``ActorRef.tell``
* ``akka.dispatch.Future[_]`` will use ``send-request-reply`` semantics, exactly like ``ActorRef.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.
@ -101,7 +101,7 @@ we *strongly* recommend that parameters passed are immutable.
One-way message send
^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: typed-actor-call-oneway
As simple as that! The method will be executed on another thread; asynchronously.
@ -109,13 +109,13 @@ As simple as that! The method will be executed on another thread; asynchronously
Request-reply message send
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/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
.. includecode:: code/akka/docs/actor/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,
@ -124,7 +124,7 @@ if needed. It will throw a ``java.util.concurrent.TimeoutException`` if a timeou
Request-reply-with-future message send
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: typed-actor-call-future
This call is asynchronous, and the Future returned can be used for asynchronous composition.
@ -134,12 +134,12 @@ 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
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: typed-actor-stop
This asynchronously stops the Typed Actor associated with the specified proxy ASAP.
.. includecode:: code/TypedActorDocSpec.scala
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala
:include: typed-actor-poisonpill
This asynchronously stops the Typed Actor associated with the specified proxy
@ -156,13 +156,20 @@ This also works for creating child Typed Actors in regular Akka Actors.
Lifecycle callbacks
-------------------
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.
By having your Typed Actor implementation class implement any and all of the following:
* ``TypedActor.PreStart``
* ``TypedActor.PostStop``
* ``TypedActor.PreRestart``
* ``TypedActor.PostRestart``
You can hook into the lifecycle 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#typed-actor-supercharge
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala#typed-actor-supercharge
.. includecode:: code/TypedActorDocSpec.scala#typed-actor-supercharge-usage
.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala#typed-actor-supercharge-usage

View file

@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor._
object BeanstalkBasedMailboxExtension extends ExtensionId[BeanstalkMailboxSettings] with ExtensionIdProvider {
override def get(system: ActorSystem): BeanstalkMailboxSettings = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl) = new BeanstalkMailboxSettings(system.settings.config)
}

View file

@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor._
object FileBasedMailboxExtension extends ExtensionId[FileBasedMailboxSettings] with ExtensionIdProvider {
override def get(system: ActorSystem): FileBasedMailboxSettings = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl) = new FileBasedMailboxSettings(system.settings.config)
}

View file

@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor._
object MongoBasedMailboxExtension extends ExtensionId[MongoBasedMailboxSettings] with ExtensionIdProvider {
override def get(system: ActorSystem): MongoBasedMailboxSettings = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl) = new MongoBasedMailboxSettings(system.settings.config)
}

View file

@ -7,6 +7,7 @@ import com.typesafe.config.Config
import akka.actor._
object RedisBasedMailboxExtension extends ExtensionId[RedisBasedMailboxSettings] with ExtensionIdProvider {
override def get(system: ActorSystem): RedisBasedMailboxSettings = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl) = new RedisBasedMailboxSettings(system.settings.config)
}

View file

@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor._
object ZooKeeperBasedMailboxExtension extends ExtensionId[ZooKeeperBasedMailboxSettings] with ExtensionIdProvider {
override def get(system: ActorSystem): ZooKeeperBasedMailboxSettings = super.get(system)
def lookup() = this
def createExtension(system: ActorSystemImpl) = new ZooKeeperBasedMailboxSettings(system.settings.config)
}

View file

@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor.{ ExtensionId, ActorSystem, Extension, ActorSystemImpl }
object TestKitExtension extends ExtensionId[TestKitSettings] {
override def get(system: ActorSystem): TestKitSettings = super.get(system)
def createExtension(system: ActorSystemImpl): TestKitSettings = new TestKitSettings(system.settings.config)
}