Merge pull request #1933 from akka/wip-3764-props-docs-∂π
clean up docs and deprecation around how to create Props
This commit is contained in:
commit
453815c073
17 changed files with 99 additions and 244 deletions
|
|
@ -22,7 +22,7 @@ public class ActorCreationTest {
|
||||||
});
|
});
|
||||||
assert false;
|
assert false;
|
||||||
} catch(IllegalArgumentException e) {
|
} catch(IllegalArgumentException e) {
|
||||||
assertEquals("cannot use non-static local Creator to create actors; make it static or top-level", e.getMessage());
|
assertEquals("cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,18 @@ public class JavaAPI {
|
||||||
ActorRef ref = system.actorOf(Props.create(JavaAPITestActor.class));
|
ActorRef ref = system.actorOf(Props.create(JavaAPITestActor.class));
|
||||||
assertNotNull(ref);
|
assertNotNull(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
public static Props mkProps() {
|
||||||
public void mustBeAbleToCreateActorRefFromFactory() {
|
return Props.create(new Creator<Actor>() {
|
||||||
ActorRef ref = system.actorOf(Props.empty().withCreator(new Creator<Actor>() {
|
|
||||||
public Actor create() {
|
public Actor create() {
|
||||||
return new JavaAPITestActor();
|
return new JavaAPITestActor();
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mustBeAbleToCreateActorRefFromFactory() {
|
||||||
|
ActorRef ref = system.actorOf(mkProps());
|
||||||
assertNotNull(ref);
|
assertNotNull(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ object SupervisorHierarchySpec {
|
||||||
val propsTemplate = Props.empty.withDispatcher("hierarchy")
|
val propsTemplate = Props.empty.withDispatcher("hierarchy")
|
||||||
(1 to kids).map { (id) ⇒
|
(1 to kids).map { (id) ⇒
|
||||||
val kidSize = if (rest > 0) { rest -= 1; sizes + 1 } else sizes
|
val kidSize = if (rest > 0) { rest -= 1; sizes + 1 } else sizes
|
||||||
val props = propsTemplate.withCreator(new Hierarchy(kidSize, breadth, listener, myLevel + 1, random))
|
val props = Props(new Hierarchy(kidSize, breadth, listener, myLevel + 1, random)).withDeploy(propsTemplate.deploy)
|
||||||
(context.watch(context.actorOf(props, id.toString)).path, kidSize)
|
(context.watch(context.actorOf(props, id.toString)).path, kidSize)
|
||||||
}(collection.breakOut)
|
}(collection.breakOut)
|
||||||
} else Map()
|
} else Map()
|
||||||
|
|
|
||||||
|
|
@ -272,11 +272,6 @@ class VerifySerializabilitySpec extends AkkaSpec(SerializationTests.verifySerial
|
||||||
val b = system.actorOf(Props(new FooActor))
|
val b = system.actorOf(Props(new FooActor))
|
||||||
system stop b
|
system stop b
|
||||||
|
|
||||||
val c = system.actorOf(Props.empty.withCreator(new UntypedActorFactory {
|
|
||||||
def create() = new FooUntypedActor
|
|
||||||
}))
|
|
||||||
system stop c
|
|
||||||
|
|
||||||
intercept[IllegalArgumentException] {
|
intercept[IllegalArgumentException] {
|
||||||
val d = system.actorOf(Props(new NonSerializableActor(system)))
|
val d = system.actorOf(Props(new NonSerializableActor(system)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ private[akka] object ActorCell {
|
||||||
|
|
||||||
final val emptyActorRefSet: Set[ActorRef] = immutable.HashSet.empty
|
final val emptyActorRefSet: Set[ActorRef] = immutable.HashSet.empty
|
||||||
|
|
||||||
final val terminatedProps: Props = Props(() ⇒ throw new IllegalActorStateException("This Actor has been terminated"))
|
final val terminatedProps: Props = Props((throw new IllegalActorStateException("This Actor has been terminated")): Actor)
|
||||||
|
|
||||||
final val undefinedUid = 0
|
final val undefinedUid = 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ object Props {
|
||||||
/**
|
/**
|
||||||
* The default Props instance, uses the settings from the Props object starting with default*.
|
* The default Props instance, uses the settings from the Props object starting with default*.
|
||||||
*/
|
*/
|
||||||
final val default = new Props()
|
final val default = Props(defaultDeploy, classOf[CreatorFunctionConsumer], List(defaultCreator))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
|
|
@ -87,36 +87,6 @@ object Props {
|
||||||
private def mkProps(classOfActor: Class[_], ctor: () ⇒ Actor): Props =
|
private def mkProps(classOfActor: Class[_], ctor: () ⇒ Actor): Props =
|
||||||
Props(classOf[TypedCreatorFunctionConsumer], classOfActor, ctor)
|
Props(classOf[TypedCreatorFunctionConsumer], classOfActor, ctor)
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
|
|
||||||
* using the supplied thunk.
|
|
||||||
*/
|
|
||||||
@deprecated("give class and arguments instead", "2.2")
|
|
||||||
def apply(creator: Creator[_ <: Actor]): Props = default.withCreator(creator.create)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The deprecated legacy constructor.
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.withDispatcher and friends", "2.2")
|
|
||||||
def apply(
|
|
||||||
creator: () ⇒ Actor = Props.defaultCreator,
|
|
||||||
dispatcher: String = Deploy.NoDispatcherGiven,
|
|
||||||
routerConfig: RouterConfig = Props.defaultRoutedProps,
|
|
||||||
deploy: Deploy = Props.defaultDeploy): Props = {
|
|
||||||
|
|
||||||
val d1 = if (dispatcher != Deploy.NoDispatcherGiven) deploy.copy(dispatcher = dispatcher) else deploy
|
|
||||||
val d2 = if (routerConfig != Props.defaultRoutedProps) d1.copy(routerConfig = routerConfig) else d1
|
|
||||||
val p = Props(classOf[CreatorFunctionConsumer], creator)
|
|
||||||
if (d2 != Props.defaultDeploy) p.withDeploy(d2) else p
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The deprecated legacy extractor.
|
|
||||||
*/
|
|
||||||
@deprecated("use three-argument version", "2.2")
|
|
||||||
def unapply(p: Props)(dummy: Int = 0): Option[(() ⇒ Actor, String, RouterConfig, Deploy)] =
|
|
||||||
Some((p.creator, p.dispatcher, p.routerConfig, p.deploy))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scala API: create a Props given a class and its constructor arguments.
|
* Scala API: create a Props given a class and its constructor arguments.
|
||||||
*/
|
*/
|
||||||
|
|
@ -133,7 +103,7 @@ object Props {
|
||||||
*/
|
*/
|
||||||
def create[T <: Actor](creator: Creator[T]): Props = {
|
def create[T <: Actor](creator: Creator[T]): Props = {
|
||||||
if ((creator.getClass.getEnclosingClass ne null) && (creator.getClass.getModifiers & Modifier.STATIC) == 0)
|
if ((creator.getClass.getEnclosingClass ne null) && (creator.getClass.getModifiers & Modifier.STATIC) == 0)
|
||||||
throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static or top-level")
|
throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level")
|
||||||
val ac = classOf[Actor]
|
val ac = classOf[Actor]
|
||||||
val actorClass = Reflect.findMarker(creator.getClass, classOf[Creator[_]]) match {
|
val actorClass = Reflect.findMarker(creator.getClass, classOf[Creator[_]]) match {
|
||||||
case t: ParameterizedType ⇒
|
case t: ParameterizedType ⇒
|
||||||
|
|
@ -199,37 +169,6 @@ final case class Props(deploy: Deploy, clazz: Class[_], args: immutable.Seq[Any]
|
||||||
// validate producer constructor signature; throws IllegalArgumentException if invalid
|
// validate producer constructor signature; throws IllegalArgumentException if invalid
|
||||||
producer
|
producer
|
||||||
|
|
||||||
/**
|
|
||||||
* No-args constructor that sets all the default values.
|
|
||||||
*
|
|
||||||
* @deprecated use `Props.create(clazz, args ...)` instead
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.create()", "2.2")
|
|
||||||
def this() = this(Props.defaultDeploy, classOf[CreatorFunctionConsumer], List(Props.defaultCreator))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java API: create Props from an [[UntypedActorFactory]]
|
|
||||||
*
|
|
||||||
* @deprecated use `Props.create(clazz, args ...)` instead; this method has been
|
|
||||||
* deprecated because it encourages creating Props which contain
|
|
||||||
* non-serializable inner classes, making them also
|
|
||||||
* non-serializable
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.create()", "2.2")
|
|
||||||
def this(factory: UntypedActorFactory) = this(Props.defaultDeploy, classOf[UntypedActorFactoryConsumer], List(factory))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java API: create Props from a given [[java.lang.Class]]
|
|
||||||
*
|
|
||||||
* @deprecated use Props.create(clazz) instead; deprecated since it duplicates
|
|
||||||
* another API
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.create()", "2.2")
|
|
||||||
def this(actorClass: Class[_ <: Actor]) = this(Props.defaultDeploy, actorClass, List.empty)
|
|
||||||
|
|
||||||
@deprecated("There is no use-case for this method anymore", "2.2")
|
|
||||||
def creator: () ⇒ Actor = newActor
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method for extracting the dispatcher information from the
|
* Convenience method for extracting the dispatcher information from the
|
||||||
* contained [[Deploy]] instance.
|
* contained [[Deploy]] instance.
|
||||||
|
|
@ -254,36 +193,6 @@ final case class Props(deploy: Deploy, clazz: Class[_], args: immutable.Seq[Any]
|
||||||
*/
|
*/
|
||||||
def routerConfig: RouterConfig = deploy.routerConfig
|
def routerConfig: RouterConfig = deploy.routerConfig
|
||||||
|
|
||||||
/**
|
|
||||||
* Scala API: Returns a new Props with the specified creator set.
|
|
||||||
*
|
|
||||||
* The creator must not return the same instance multiple times.
|
|
||||||
*/
|
|
||||||
@deprecated("use Props(...).withDeploy(other.deploy)", "2.2")
|
|
||||||
def withCreator(c: ⇒ Actor): Props = copy(clazz = classOf[CreatorFunctionConsumer], args = (() ⇒ c) :: Nil)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java API: Returns a new Props with the specified creator set.
|
|
||||||
*
|
|
||||||
* The creator must not return the same instance multiple times.
|
|
||||||
*
|
|
||||||
* @deprecated use `Props.create(clazz, args ...)` instead; this method has been
|
|
||||||
* deprecated because it encourages creating Props which contain
|
|
||||||
* non-serializable inner classes, making them also
|
|
||||||
* non-serializable
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.create(clazz, args ...).withDeploy(other.deploy) instead", "2.2")
|
|
||||||
def withCreator(c: Creator[Actor]): Props = copy(clazz = classOf[CreatorConsumer], args = classOf[Actor] :: c :: Nil)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new Props with the specified creator set.
|
|
||||||
*
|
|
||||||
* @deprecated use Props.create(clazz) instead; deprecated since it duplicates
|
|
||||||
* another API
|
|
||||||
*/
|
|
||||||
@deprecated("use Props.create(clazz, args).withDeploy(other.deploy)", "2.2")
|
|
||||||
def withCreator(c: Class[_ <: Actor]): Props = copy(clazz = c, args = Nil)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new Props with the specified dispatcher set.
|
* Returns a new Props with the specified dispatcher set.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ trait TypedActorFactory {
|
||||||
val proxyVar = new AtomVar[R] //Chicken'n'egg-resolver
|
val proxyVar = new AtomVar[R] //Chicken'n'egg-resolver
|
||||||
val c = props.creator //Cache this to avoid closing over the Props
|
val c = props.creator //Cache this to avoid closing over the Props
|
||||||
val i = props.interfaces //Cache this to avoid closing over the Props
|
val i = props.interfaces //Cache this to avoid closing over the Props
|
||||||
val ap = props.actorProps.withCreator(new TypedActor.TypedActor[R, T](proxyVar, c(), i))
|
val ap = Props(new TypedActor.TypedActor[R, T](proxyVar, c(), i)).withDeploy(props.actorProps.deploy)
|
||||||
typedActor.createActorRefProxy(props, proxyVar, actorFactory.actorOf(ap))
|
typedActor.createActorRefProxy(props, proxyVar, actorFactory.actorOf(ap))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ trait TypedActorFactory {
|
||||||
val proxyVar = new AtomVar[R] //Chicken'n'egg-resolver
|
val proxyVar = new AtomVar[R] //Chicken'n'egg-resolver
|
||||||
val c = props.creator //Cache this to avoid closing over the Props
|
val c = props.creator //Cache this to avoid closing over the Props
|
||||||
val i = props.interfaces //Cache this to avoid closing over the Props
|
val i = props.interfaces //Cache this to avoid closing over the Props
|
||||||
val ap = props.actorProps.withCreator(new akka.actor.TypedActor.TypedActor[R, T](proxyVar, c(), i))
|
val ap = Props(new akka.actor.TypedActor.TypedActor[R, T](proxyVar, c(), i)).withDeploy(props.actorProps.deploy)
|
||||||
typedActor.createActorRefProxy(props, proxyVar, actorFactory.actorOf(ap, name))
|
typedActor.createActorRefProxy(props, proxyVar, actorFactory.actorOf(ap, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -619,7 +619,7 @@ case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
||||||
def actorProps(): Props =
|
def actorProps(): Props =
|
||||||
if (dispatcher == Props.default.dispatcher)
|
if (dispatcher == Props.default.dispatcher)
|
||||||
Props.default.withDeploy(deploy)
|
Props.default.withDeploy(deploy)
|
||||||
else Props(dispatcher = dispatcher).withDeploy(deploy)
|
else Props.default.withDispatcher(dispatcher).withDeploy(deploy)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,11 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
case _: ProducesMessageQueue[_] if system.mailboxes.hasRequiredType(actorClass) ⇒
|
case _: ProducesMessageQueue[_] if system.mailboxes.hasRequiredType(actorClass) ⇒
|
||||||
val req = system.mailboxes.getRequiredType(actorClass)
|
val req = system.mailboxes.getRequiredType(actorClass)
|
||||||
if (req isInstance mbox.messageQueue) Create(None)
|
if (req isInstance mbox.messageQueue) Create(None)
|
||||||
else Create(Some(ActorInitializationException(self,
|
else {
|
||||||
s"Actor [$self] requires mailbox type [$req] got [${mbox.messageQueue.getClass.getName}]")))
|
val gotType = if (mbox.messageQueue == null) "null" else mbox.messageQueue.getClass.getName
|
||||||
|
Create(Some(ActorInitializationException(self,
|
||||||
|
s"Actor [$self] requires mailbox type [$req] got [$gotType]")))
|
||||||
|
}
|
||||||
case _ ⇒ Create(None)
|
case _ ⇒ Create(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,17 @@ import static akka.pattern.Patterns.pipe;
|
||||||
import static akka.pattern.Patterns.gracefulStop;
|
import static akka.pattern.Patterns.gracefulStop;
|
||||||
//#import-gracefulStop
|
//#import-gracefulStop
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||||
|
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
//#import-gracefulStop
|
//#import-gracefulStop
|
||||||
import scala.concurrent.Await;
|
import scala.concurrent.Await;
|
||||||
//#import-ask
|
//#import-ask
|
||||||
|
|
@ -119,43 +122,6 @@ public class UntypedActorDocTest {
|
||||||
}
|
}
|
||||||
//#parametric-creator
|
//#parametric-creator
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Test
|
|
||||||
public void createPropsDeprecated() {
|
|
||||||
//#creating-props-deprecated
|
|
||||||
// DEPRECATED: encourages to close over enclosing class
|
|
||||||
final Props props1 = new Props(new UntypedActorFactory() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
@Override
|
|
||||||
public UntypedActor create() throws Exception {
|
|
||||||
return new MyUntypedActor();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// DEPRECATED: encourages to close over enclosing class
|
|
||||||
final Props props2 = new Props().withCreator(new UntypedActorFactory() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
@Override
|
|
||||||
public UntypedActor create() throws Exception {
|
|
||||||
return new MyUntypedActor();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// these are DEPRECATED due to duplicate functionality with Props.create()
|
|
||||||
final Props props3 = new Props(MyUntypedActor.class);
|
|
||||||
final Props props4 = new Props().withCreator(MyUntypedActor.class);
|
|
||||||
//#creating-props-deprecated
|
|
||||||
new JavaTestKit(system) {
|
|
||||||
{
|
|
||||||
for (Props props : new Props[] { props1, props2, props3, props4 }) {
|
|
||||||
final ActorRef a = system.actorOf(props);
|
|
||||||
a.tell("hello", getRef());
|
|
||||||
expectMsgEquals("hello");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void systemActorOf() {
|
public void systemActorOf() {
|
||||||
//#system-actorOf
|
//#system-actorOf
|
||||||
|
|
@ -538,8 +504,15 @@ public class UntypedActorDocTest {
|
||||||
* @return a Props for creating this actor, which can then be further configured
|
* @return a Props for creating this actor, which can then be further configured
|
||||||
* (e.g. calling `.withDispatcher()` on it)
|
* (e.g. calling `.withDispatcher()` on it)
|
||||||
*/
|
*/
|
||||||
public static Props props(int magicNumber) {
|
public static Props props(final int magicNumber) {
|
||||||
return Props.create(DemoActor.class, magicNumber);
|
return Props.create(new Creator<DemoActor>() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DemoActor create() throws Exception {
|
||||||
|
return new DemoActor(magicNumber);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final int magicNumber;
|
final int magicNumber;
|
||||||
|
|
|
||||||
|
|
@ -65,52 +65,22 @@ example of a parametric factory could be:
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#parametric-creator
|
.. includecode:: code/docs/actor/UntypedActorDocTest.java#parametric-creator
|
||||||
|
|
||||||
Deprecated Variants
|
.. note::
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Up to Akka 2.1 there were also the following possibilities (which are retained
|
In order for mailbox requirements—like using a deque-based mailbox for actors
|
||||||
for a migration period):
|
using the stash—to be picked up, the actor type needs to be known before
|
||||||
|
creating it, which is what the :class:`Creator` type argument allows.
|
||||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#import-untypedActor
|
Therefore make sure to use the specific type for your actors wherever
|
||||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#creating-props-deprecated
|
possible.
|
||||||
|
|
||||||
The last two are deprecated because their functionality is available in full
|
|
||||||
through :meth:`Props.create()`.
|
|
||||||
|
|
||||||
The first two are deprecated because the resulting :class:`UntypedActorFactory`
|
|
||||||
is typically a local class which means that it implicitly carries a reference
|
|
||||||
to the enclosing class. This can easily make the resulting :class:`Props`
|
|
||||||
non-serializable, e.g. when the enclosing class is an :class:`Actor`. Akka
|
|
||||||
advocates location transparency, meaning that an application written with
|
|
||||||
actors should just work when it is deployed over multiple network nodes, and
|
|
||||||
non-serializable actor factories would break this principle. In case indirect
|
|
||||||
actor creation is needed—for example when using dependency injection—there is
|
|
||||||
the possibility to use an :class:`IndirectActorProducer` as described below.
|
|
||||||
|
|
||||||
There were two use-cases for these methods: passing constructor arguments to
|
|
||||||
the actor—which is solved by the newly introduced :meth:`Props.create()` method
|
|
||||||
above—and creating actors “on the spot” as anonymous classes. The latter should
|
|
||||||
be solved by making these actors named inner classes instead (if they are not
|
|
||||||
``static`` then the enclosing instance’s ``this`` reference needs to be passed
|
|
||||||
as the first argument).
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Declaring one actor within another is very dangerous and breaks actor
|
|
||||||
encapsulation unless the nested actor is a static inner class. Never pass an
|
|
||||||
actor’s ``this`` reference into :class:`Props`!
|
|
||||||
|
|
||||||
Recommended Practices
|
Recommended Practices
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
It is a good idea to provide static factory methods on the
|
It is a good idea to provide static factory methods on the
|
||||||
:class:`UntypedActor` which help keeping the creation of suitable
|
:class:`UntypedActor` which help keeping the creation of suitable
|
||||||
:class:`Props` as close to the actor definition as possible, thus containing
|
:class:`Props` as close to the actor definition as possible. This also allows
|
||||||
the gap in type-safety introduced by reflective instantiation within a single
|
usage of the :class:`Creator`-based methods which statically verify that the
|
||||||
class instead of spreading it out across a whole code-base. This helps
|
used constructor actually exists instead relying only on a runtime check.
|
||||||
especially when refactoring the actor’s constructor signature at a later point,
|
|
||||||
where compiler checks will allow this modification to be done with greater
|
|
||||||
confidence than without.
|
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/UntypedActorDocTest.java#props-factory
|
.. includecode:: code/docs/actor/UntypedActorDocTest.java#props-factory
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ The following, previously deprecated, features have been removed:
|
||||||
* `event-handlers renamed to loggers <http://doc.akka.io/docs/akka/2.2.3/project/migration-guide-2.1.x-2.2.x.html#event-handlers_renamed_to_loggers>`_
|
* `event-handlers renamed to loggers <http://doc.akka.io/docs/akka/2.2.3/project/migration-guide-2.1.x-2.2.x.html#event-handlers_renamed_to_loggers>`_
|
||||||
* `API changes to FSM and TestFSMRef <http://doc.akka.io/docs/akka/2.2.3/project/migration-guide-2.1.x-2.2.x.html#API_changes_to_FSM_and_TestFSMRef>`_
|
* `API changes to FSM and TestFSMRef <http://doc.akka.io/docs/akka/2.2.3/project/migration-guide-2.1.x-2.2.x.html#API_changes_to_FSM_and_TestFSMRef>`_
|
||||||
* DefaultScheduler superseded by LightArrayRevolverScheduler
|
* DefaultScheduler superseded by LightArrayRevolverScheduler
|
||||||
|
* all previously deprecated construction and deconstruction methods for Props
|
||||||
|
|
||||||
publishCurrentClusterState is Deprecated
|
publishCurrentClusterState is Deprecated
|
||||||
========================================
|
========================================
|
||||||
|
|
|
||||||
|
|
@ -71,43 +71,37 @@ dispatcher to use, see more below). Here are some examples of how to create a
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/ActorDocSpec.scala#creating-props
|
.. includecode:: code/docs/actor/ActorDocSpec.scala#creating-props
|
||||||
|
|
||||||
The last line shows how to pass constructor arguments to the :class:`Actor`
|
The second variant shows how to pass constructor arguments to the
|
||||||
being created. The presence of a matching constructor is verified during
|
:class:`Actor` being created, but it should only be used outside of actors as
|
||||||
construction of the :class:`Props` object, resulting in an
|
explained below.
|
||||||
|
|
||||||
|
The last line shows a possibility to pass constructor arguments regardless of
|
||||||
|
the context it is being used in. The presence of a matching constructor is
|
||||||
|
verified during construction of the :class:`Props` object, resulting in an
|
||||||
:class:`IllegalArgumentEception` if no or multiple matching constructors are
|
:class:`IllegalArgumentEception` if no or multiple matching constructors are
|
||||||
found.
|
found.
|
||||||
|
|
||||||
Deprecated Variants
|
Dangerous Variants
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Up to Akka 2.1 there were also the following possibilities (which are retained
|
|
||||||
for a migration period):
|
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/ActorDocSpec.scala#creating-props-deprecated
|
.. includecode:: code/docs/actor/ActorDocSpec.scala#creating-props-deprecated
|
||||||
|
|
||||||
The first one is deprecated because the case class structure changed between
|
This method is not recommended to be used within another actor because it
|
||||||
Akka 2.1 and 2.2.
|
encourages to close over the enclosing scope, resulting in non-serializable
|
||||||
|
|
||||||
The two variants in the middle are deprecated because :class:`Props` are
|
|
||||||
primarily concerned with actor creation and thus the “creator” part should be
|
|
||||||
explicitly set when creating an instance. In case you want to deploy one actor
|
|
||||||
in the same was as another, simply use
|
|
||||||
``Props(...).withDeploy(otherProps.deploy)``.
|
|
||||||
|
|
||||||
The last one is not technically deprecated, but it is not recommended because
|
|
||||||
it encourages to close over the enclosing scope, resulting in non-serializable
|
|
||||||
:class:`Props` and possibly race conditions (breaking the actor encapsulation).
|
:class:`Props` and possibly race conditions (breaking the actor encapsulation).
|
||||||
We will provide a macro-based solution in a future release which allows similar
|
We will provide a macro-based solution in a future release which allows similar
|
||||||
syntax without the headaches, at which point this variant will be properly
|
syntax without the headaches, at which point this variant will be properly
|
||||||
deprecated.
|
deprecated. On the other hand using this variant in a :class:`Props` factory in
|
||||||
|
the actor’s companion object as documented under “Recommended Practices” below
|
||||||
|
is completely fine.
|
||||||
|
|
||||||
There were two use-cases for these methods: passing constructor arguments to
|
There were two use-cases for these methods: passing constructor arguments to
|
||||||
the actor—which is solved by the newly introduced
|
the actor—which is solved by the newly introduced
|
||||||
:meth:`Props.apply(clazz, args)` method above—and creating actors “on the spot”
|
:meth:`Props.apply(clazz, args)` method above or the recommended practice
|
||||||
as anonymous classes. The latter should be solved by making these actors named
|
below—and creating actors “on the spot” as anonymous classes. The latter should
|
||||||
inner classes instead (if they are not declared within a top-level ``object``
|
be solved by making these actors named classes instead (if they are not
|
||||||
then the enclosing instance’s ``this`` reference needs to be passed as the
|
declared within a top-level ``object`` then the enclosing instance’s ``this``
|
||||||
first argument).
|
reference needs to be passed as the first argument).
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
@ -119,12 +113,10 @@ Recommended Practices
|
||||||
|
|
||||||
It is a good idea to provide factory methods on the companion object of each
|
It is a good idea to provide factory methods on the companion object of each
|
||||||
:class:`Actor` which help keeping the creation of suitable :class:`Props` as
|
:class:`Actor` which help keeping the creation of suitable :class:`Props` as
|
||||||
close to the actor definition as possible, thus containing the gap in
|
close to the actor definition as possible. This also avoids the pitfalls
|
||||||
type-safety introduced by reflective instantiation within a single class
|
associated with using the ``Props.apply(...)`` method which takes a by-name
|
||||||
instead of spreading it out across a whole code-base. This helps especially
|
argument, since within a companion object the given code block will not retain
|
||||||
when refactoring the actor’s constructor signature at a later point, where
|
a reference to its enclosing scope:
|
||||||
compiler checks will allow this modification to be done with greater confidence
|
|
||||||
than without.
|
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/ActorDocSpec.scala#props-factory
|
.. includecode:: code/docs/actor/ActorDocSpec.scala#props-factory
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class DemoActorWrapper extends Actor {
|
||||||
* @return a Props for creating this actor, which can then be further configured
|
* @return a Props for creating this actor, which can then be further configured
|
||||||
* (e.g. calling `.withDispatcher()` on it)
|
* (e.g. calling `.withDispatcher()` on it)
|
||||||
*/
|
*/
|
||||||
def props(magicNumber: Int): Props = Props(classOf[DemoActor], magicNumber)
|
def props(magicNumber: Int): Props = Props(new DemoActor(magicNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
class DemoActor(magicNumber: Int) extends Actor {
|
class DemoActor(magicNumber: Int) extends Actor {
|
||||||
|
|
@ -68,9 +68,16 @@ class DemoActorWrapper extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
class SomeOtherActor extends Actor {
|
||||||
|
// Props(new DemoActor(42)) would not be safe
|
||||||
context.actorOf(DemoActor.props(42), "demo")
|
context.actorOf(DemoActor.props(42), "demo")
|
||||||
|
// ...
|
||||||
|
//#props-factory
|
||||||
|
def receive = {
|
||||||
|
case msg =>
|
||||||
|
}
|
||||||
|
//#props-factory
|
||||||
|
}
|
||||||
//#props-factory
|
//#props-factory
|
||||||
|
|
||||||
def receive = Actor.emptyBehavior
|
def receive = Actor.emptyBehavior
|
||||||
|
|
@ -239,22 +246,13 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
|
|
||||||
val props1 = Props[MyActor]
|
val props1 = Props[MyActor]
|
||||||
|
val props2 = Props(new ActorWithArgs("arg")) // careful, see below
|
||||||
val props3 = Props(classOf[ActorWithArgs], "arg")
|
val props3 = Props(classOf[ActorWithArgs], "arg")
|
||||||
//#creating-props
|
//#creating-props
|
||||||
|
|
||||||
//#creating-props-deprecated
|
//#creating-props-deprecated
|
||||||
// DEPRECATED: old case class signature
|
// NOT RECOMMENDED within another actor:
|
||||||
val props4 = Props(
|
// encourages to close over enclosing class
|
||||||
creator = { () => new MyActor },
|
|
||||||
dispatcher = "my-dispatcher")
|
|
||||||
|
|
||||||
// DEPRECATED due to duplicate functionality with Props.apply()
|
|
||||||
val props5 = props1.withCreator(new MyActor)
|
|
||||||
|
|
||||||
// DEPRECATED due to duplicate functionality with Props.apply()
|
|
||||||
val props6 = props1.withCreator(classOf[MyActor])
|
|
||||||
|
|
||||||
// NOT RECOMMENDED: encourages to close over enclosing class
|
|
||||||
val props7 = Props(new MyActor)
|
val props7 = Props(new MyActor)
|
||||||
//#creating-props-deprecated
|
//#creating-props-deprecated
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +303,10 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
"helloBean")
|
"helloBean")
|
||||||
//#creating-indirectly
|
//#creating-indirectly
|
||||||
}
|
}
|
||||||
val actorRef = a.actorRef
|
val actorRef = {
|
||||||
|
import scala.language.reflectiveCalls
|
||||||
|
a.actorRef
|
||||||
|
}
|
||||||
|
|
||||||
val message = 42
|
val message = 42
|
||||||
implicit val self = testActor
|
implicit val self = testActor
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class DaemonMsgCreateSerializerSpec extends AkkaSpec {
|
||||||
"serialize and de-serialize DaemonMsgCreate with function creator" in {
|
"serialize and de-serialize DaemonMsgCreate with function creator" in {
|
||||||
verifySerialization {
|
verifySerialization {
|
||||||
DaemonMsgCreate(
|
DaemonMsgCreate(
|
||||||
props = Props.empty.withCreator(new MyActor),
|
props = Props(new MyActor),
|
||||||
deploy = Deploy(),
|
deploy = Deploy(),
|
||||||
path = "foo",
|
path = "foo",
|
||||||
supervisor = supervisor)
|
supervisor = supervisor)
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,12 @@ class CallingThreadMailbox(_receiver: akka.actor.Cell, val mailboxType: MailboxT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only a marker to be put in the messageQueue’s stead to make error
|
||||||
|
* messages pertaining to violated mailbox type requirements less cryptic.
|
||||||
|
*/
|
||||||
|
override val messageQueue: MessageQueue = q.get
|
||||||
|
|
||||||
override def enqueue(receiver: ActorRef, msg: Envelope): Unit = q.get.enqueue(receiver, msg)
|
override def enqueue(receiver: ActorRef, msg: Envelope): Unit = q.get.enqueue(receiver, msg)
|
||||||
override def dequeue(): Envelope = throw new UnsupportedOperationException("CallingThreadMailbox cannot dequeue normally")
|
override def dequeue(): Envelope = throw new UnsupportedOperationException("CallingThreadMailbox cannot dequeue normally")
|
||||||
override def hasMessages: Boolean = q.get.hasMessages
|
override def hasMessages: Boolean = q.get.hasMessages
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,9 @@ object TestActorRef {
|
||||||
"$" + akka.util.Helpers.base64(l)
|
"$" + akka.util.Helpers.base64(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply[T <: Actor](factory: ⇒ T)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props.empty.withCreator(factory), randomName)
|
def apply[T <: Actor: ClassTag](factory: ⇒ T)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props(factory), randomName)
|
||||||
|
|
||||||
def apply[T <: Actor](factory: ⇒ T, name: String)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props.empty.withCreator(factory), name)
|
def apply[T <: Actor: ClassTag](factory: ⇒ T, name: String)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props(factory), name)
|
||||||
|
|
||||||
def apply[T <: Actor](props: Props)(implicit system: ActorSystem): TestActorRef[T] = apply[T](props, randomName)
|
def apply[T <: Actor](props: Props)(implicit system: ActorSystem): TestActorRef[T] = apply[T](props, randomName)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import akka.dispatch.DispatcherPrerequisites
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import akka.dispatch.MessageDispatcher
|
import akka.dispatch.MessageDispatcher
|
||||||
import akka.dispatch.MailboxType
|
import akka.dispatch.MailboxType
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a specialised form of the TestActorRef with support for querying and
|
* This is a specialised form of the TestActorRef with support for querying and
|
||||||
|
|
@ -89,13 +90,13 @@ class TestFSMRef[S, D, T <: Actor](
|
||||||
|
|
||||||
object TestFSMRef {
|
object TestFSMRef {
|
||||||
|
|
||||||
def apply[S, D, T <: Actor](factory: ⇒ T)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
def apply[S, D, T <: Actor: ClassTag](factory: ⇒ T)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
||||||
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
||||||
new TestFSMRef(impl, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], TestActorRef.randomName)
|
new TestFSMRef(impl, Props(factory), impl.guardian.asInstanceOf[InternalActorRef], TestActorRef.randomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply[S, D, T <: Actor](factory: ⇒ T, name: String)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
def apply[S, D, T <: Actor: ClassTag](factory: ⇒ T, name: String)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
||||||
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
||||||
new TestFSMRef(impl, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], name)
|
new TestFSMRef(impl, Props(factory), impl.guardian.asInstanceOf[InternalActorRef], name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue