deprecate closure-taking Props factories, see #3081

- base Props on Deploy, Class and Seq[Any] (i.e. constructor args)
- remove deprecated Props usage from akka-docs sample code
- rewrite UntypedActorDocTestBase
- rewrite Java/Scala doc section on actor creation
- add migration guide entry
This commit is contained in:
Roland 2013-04-14 22:56:41 +02:00
parent 64f664a706
commit 28aad82b1a
80 changed files with 2268 additions and 1641 deletions

View file

@ -9,13 +9,16 @@ import akka.japi.Creator
import scala.reflect.ClassTag
import akka.routing._
import akka.util.Reflect
import scala.annotation.varargs
import Deploy.NoDispatcherGiven
import scala.collection.immutable
/**
* Factory for Props instances.
*
* Props is a ActorRef configuration object, that is immutable, so it is thread safe and fully sharable.
*
* Used when creating new actors through; <code>ActorSystem.actorOf</code> and <code>ActorContext.actorOf</code>.
* Used when creating new actors through <code>ActorSystem.actorOf</code> and <code>ActorContext.actorOf</code>.
*/
object Props {
@ -34,30 +37,32 @@ object Props {
*/
final val defaultDeploy = Deploy()
/**
* A Props instance whose creator will create an actor that doesn't respond to any message
*/
final val empty = new Props(() new Actor { def receive = Actor.emptyBehavior })
/**
* The default Props instance, uses the settings from the Props object starting with default*.
*/
final val default = new Props()
/**
* A Props instance whose creator will create an actor that doesn't respond to any message
*/
final val empty = Props[EmptyActor]
/**
* INTERNAL API
*
* (Not because it is so immensely complicated, only because we might remove it if no longer needed internally)
*/
private[akka] class EmptyActor extends Actor {
def receive = Actor.emptyBehavior
}
/**
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
* of the supplied type using the default constructor.
*
* Scala API.
*/
def apply[T <: Actor: ClassTag](): Props =
default.withCreator(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[_ <: Actor]])
/**
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
* of the supplied class using the default constructor.
*/
def apply(actorClass: Class[_ <: Actor]): Props = default.withCreator(actorClass)
def apply[T <: Actor: ClassTag](): Props = apply(implicitly[ClassTag[T]].runtimeClass)
/**
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
@ -65,116 +70,242 @@ object Props {
*
* Scala API.
*/
@deprecated("give class and arguments instead", "2.2")
def apply(creator: Actor): Props = default.withCreator(creator)
/**
* 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("give class and arguments instead", "2.2")
def apply(
creator: () Actor = Props.defaultCreator,
dispatcher: String = Dispatchers.DefaultDispatcherId,
routerConfig: RouterConfig = Props.defaultRoutedProps,
deploy: Deploy = Props.defaultDeploy): Props = {
val d1 = if (dispatcher != Dispatchers.DefaultDispatcherId) 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
}
/**
* Scala API: create a Props given a class and its constructor arguments.
*/
def apply(clazz: Class[_], args: Any*): Props = apply(defaultDeploy, clazz, args.toVector)
/**
* Java API: create a Props given a class and its constructor arguments.
*/
@varargs
def create(clazz: Class[_], args: AnyRef*): Props = apply(defaultDeploy, clazz, args.toVector)
}
/**
* Props is a ActorRef configuration object, that is immutable, so it is thread safe and fully sharable.
* Used when creating new actors through; <code>ActorSystem.actorOf</code> and <code>ActorContext.actorOf</code>.
*
* In case of providing code which creates the actual Actor instance, that must not return the same instance multiple times.
* Props is a configuration object using in creating an [[Actor]]; it is
* immutable, so it is thread-safe and fully shareable.
*
* Examples on Scala API:
* {{{
* val props = Props.empty
* val props = Props[MyActor]
* val props = Props(new MyActor)
* val props = Props(
* creator = ..,
* dispatcher = ..,
* routerConfig = ..
* )
* val props = Props().withCreator(new MyActor)
* val props = Props[MyActor].withRouter(RoundRobinRouter(..))
* val props = Props(classOf[MyActor], arg1, arg2)
*
* val otherProps = props.withDispatcher("dispatcher-id")
* val otherProps = props.withDeploy(<deployment info>)
* }}}
*
* Examples on Java API:
* {{{
* Props props = new Props();
* Props props = new Props(MyActor.class);
* Props props = new Props(new UntypedActorFactory() {
* public UntypedActor create() {
* return new MyActor();
* }
* });
* Props props = new Props().withCreator(new UntypedActorFactory() { ... });
* Props props = new Props(MyActor.class).withRouter(new RoundRobinRouter(..));
* final Props props = Props.empty();
* final Props props = Props.create(MyActor.class, arg1, arg2);
*
* final Props otherProps = props.withDispatcher("dispatcher-id");
* final Props otherProps = props.withDeploy(<deployment info>);
* }}}
*/
@SerialVersionUID(1L)
case class Props(
creator: () Actor = Props.defaultCreator,
dispatcher: String = Dispatchers.DefaultDispatcherId,
routerConfig: RouterConfig = Props.defaultRoutedProps,
deploy: Deploy = Props.defaultDeploy) {
@SerialVersionUID(2L)
case class Props(deploy: Deploy, clazz: Class[_], args: immutable.Seq[Any]) {
// validate constructor signature; throws IllegalArgumentException if invalid
Reflect.findConstructor(clazz, args)
/**
* No-args constructor that sets all the default values.
*
* @deprecated use `Props.create(clazz, args ...)` instead
*/
def this() = this(
creator = Props.defaultCreator,
dispatcher = Dispatchers.DefaultDispatcherId)
@deprecated("use Props.create()", "2.2")
def this() = this(Props.defaultDeploy, classOf[CreatorFunctionConsumer], Vector(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
*/
def this(factory: UntypedActorFactory) = this(
creator = () factory.create(),
dispatcher = Dispatchers.DefaultDispatcherId)
@deprecated("use constructor which takes the actor class directly", "2.2")
def this(factory: UntypedActorFactory) = this(Props.defaultDeploy, classOf[UntypedActorFactoryConsumer], Vector(factory))
/**
* Java API: create Props from a given [[Class]]
*
* @deprecated use Props.create(clazz) instead; deprecated since it duplicates
* another API
*/
def this(actorClass: Class[_ <: Actor]) = this(
creator = FromClassCreator(actorClass),
dispatcher = Dispatchers.DefaultDispatcherId,
routerConfig = Props.defaultRoutedProps)
@deprecated("use Props.create()", "2.2")
def this(actorClass: Class[_ <: Actor]) = this(Props.defaultDeploy, actorClass, Vector.empty)
@deprecated("use newActor()", "2.2")
def creator: () Actor = newActor
/**
* Convenience method for extracting the dispatcher information from the
* contained [[Deploy]] instance.
*/
def dispatcher: String = deploy.dispatcher match {
case NoDispatcherGiven Dispatchers.DefaultDispatcherId
case x x
}
/**
* Convenience method for extracting the router configuration from the
* contained [[Deploy]] instance.
*/
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.
*/
def withCreator(c: Actor): Props = copy(creator = () c)
@deprecated("move actor into named class and use withCreator(clazz)", "2.2")
def withCreator(c: Actor): Props = copy(clazz = classOf[CreatorFunctionConsumer], args = Vector(() c))
/**
* 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
*/
def withCreator(c: Creator[Actor]): Props = copy(creator = () c.create)
@deprecated("use Props.create(clazz, args ...) instead", "2.2")
def withCreator(c: Creator[Actor]): Props = copy(clazz = classOf[CreatorConsumer], args = Vector(c))
/**
* Java API: Returns a new Props with the specified creator set.
* Returns a new Props with the specified creator set.
*
* @deprecated use Props.create(clazz) instead; deprecated since it duplicates
* another API
*/
def withCreator(c: Class[_ <: Actor]): Props = copy(creator = FromClassCreator(c))
@deprecated("use Props(clazz, args).withDeploy(other.deploy)", "2.2")
def withCreator(c: Class[_ <: Actor]): Props = copy(clazz = c, args = Vector.empty)
/**
* Returns a new Props with the specified dispatcher set.
*/
def withDispatcher(d: String): Props = copy(dispatcher = d)
def withDispatcher(d: String): Props = copy(deploy = deploy.copy(dispatcher = d))
/**
* Returns a new Props with the specified router config set.
*/
def withRouter(r: RouterConfig): Props = copy(routerConfig = r)
def withRouter(r: RouterConfig): Props = copy(deploy = deploy.copy(routerConfig = r))
/**
* Returns a new Props with the specified deployment configuration.
*/
def withDeploy(d: Deploy): Props = copy(deploy = d)
def withDeploy(d: Deploy): Props = copy(deploy = d withFallback deploy)
/**
* Create a new actor instance. This method is only useful when called during
* actor creation by the ActorSystem, i.e. for user-level code it can only be
* used within the implementation of [[IndirectActorProducer#produce]].
*/
def newActor(): Actor = {
Reflect.instantiate(clazz, args) match {
case a: Actor a
case i: IndirectActorProducer i.produce()
case _ throw new IllegalArgumentException(s"unknown actor creator [$clazz]")
}
}
/**
* Obtain an upper-bound approximation of the actor class which is going to
* be created by these Props. In other words, the [[#newActor]] method will
* produce an instance of this class or a subclass thereof. This is used by
* the actor system to select special dispatchers or mailboxes in case
* dependencies are encoded in the actor type.
*/
def actorClass(): Class[_ <: Actor] = {
if (classOf[IndirectActorProducer].isAssignableFrom(clazz)) {
Reflect.instantiate(clazz, args).asInstanceOf[IndirectActorProducer].actorClass
} else if (classOf[Actor].isAssignableFrom(clazz)) {
clazz.asInstanceOf[Class[_ <: Actor]]
} else {
throw new IllegalArgumentException("unknown actor creator [$clazz]")
}
}
}
/**
* Used when creating an Actor from a class. Special Function0 to be
* able to optimize serialization.
* This interface defines a class of actor creation strategies deviating from
* the usual default of just reflectively instantiating the [[Actor]]
* subclass. It can be used to allow a dependency injection framework to
* determine the actual actor class and how it shall be instantiated.
*/
private[akka] case class FromClassCreator(clazz: Class[_ <: Actor]) extends Function0[Actor] {
def apply(): Actor = Reflect.instantiate(clazz)
trait IndirectActorProducer {
/**
* This factory method must produce a fresh actor instance upon each
* invocation. <b>It is not permitted to return the same instance more than
* once.</b>
*/
def produce(): Actor
/**
* This method is used by [[Props]] to determine the type of actor which will
* be created. This means that an instance of this `IndirectActorProducer`
* will be created in order to call this method during any call to
* [[Props#actorClass]]; it should be noted that such calls may
* performed during actor set-up before the actual actors instantiation, and
* that the instance created for calling `actorClass` is not necessarily reused
* later to produce the actor.
*/
def actorClass: Class[_ <: Actor]
}
/**
* INTERNAL API
*/
private[akka] class UntypedActorFactoryConsumer(factory: UntypedActorFactory) extends IndirectActorProducer {
override def actorClass = classOf[Actor]
override def produce() = factory.create()
}
/**
* INTERNAL API
*/
private[akka] class CreatorFunctionConsumer(creator: () Actor) extends IndirectActorProducer {
override def actorClass = classOf[Actor]
override def produce() = creator()
}
/**
* INTERNAL API
*/
private[akka] class CreatorConsumer(creator: Creator[Actor]) extends IndirectActorProducer {
override def actorClass = classOf[Actor]
override def produce() = creator.create()
}