alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Akka/Scala example source code file (Props.scala)

This example Akka source code file (Props.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Akka and Scala source code examples by using tags.

All credit for the original source code belongs to akka.io; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Akka tags/keywords

actor, akka, annotation, class, collection, creator, deploy, dispatch, illegalargumentexception, indirectactorproducer, props, reflection, routerconfig, routing, string, t

The Props.scala Akka example source code

/**
 * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
 */

package akka.actor

import akka.dispatch._
import akka.japi.Creator
import scala.reflect.ClassTag
import akka.routing._
import akka.util.Reflect
import scala.annotation.varargs
import Deploy.{ NoDispatcherGiven, NoMailboxGiven }
import scala.collection.immutable
import scala.language.existentials
import java.lang.reflect.Constructor
import java.lang.reflect.Modifier
import scala.annotation.tailrec
import java.lang.reflect.ParameterizedType
import java.lang.reflect.TypeVariable

/**
 * 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>.
 */
object Props {

  /**
   * The defaultCreator, simply throws an UnsupportedOperationException when applied, which is used when creating a Props
   */
  final val defaultCreator: () ⇒ Actor = () ⇒ throw new UnsupportedOperationException("No actor creator specified!")

  /**
   * The defaultRoutedProps is NoRouter which is used when creating a Props
   */
  final val defaultRoutedProps: RouterConfig = NoRouter

  /**
   * The default Deploy instance which is used when creating a Props
   */
  final val defaultDeploy = Deploy()

  /**
   * A Props instance whose creator will create an actor that doesn't respond to any message
   */
  final val empty = Props[EmptyActor]

  /**
   * The default Props instance, uses the settings from the Props object starting with default*.
   */
  final val default = Props(defaultDeploy, classOf[CreatorFunctionConsumer], List(defaultCreator))

  /**
   * 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
  }

  /**
   * Scala API: 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.
   */
  def apply[T <: Actor: ClassTag](): Props = apply(defaultDeploy, implicitly[ClassTag[T]].runtimeClass, List.empty)

  /**
   * Scala API: Returns a Props that has default values except for "creator" which will be a function that creates an instance
   * using the supplied thunk.
   *
   * CAVEAT: Required mailbox type cannot be detected when using anonymous mixin composition
   * when creating the instance. For example, the following will not detect the need for
   * `DequeBasedMessageQueueSemantics` as defined in `Stash`:
   * {{{
   * 'Props(new Actor with Stash { ... })
   * }}}
   * Instead you must create a named class that mixin the trait,
   * e.g. `class MyActor extends Actor with Stash`.
   */
  def apply[T <: Actor: ClassTag](creator: ⇒ T): Props =
    mkProps(implicitly[ClassTag[T]].runtimeClass, () ⇒ creator)

  private def mkProps(classOfActor: Class[_], ctor: () ⇒ Actor): Props =
    Props(classOf[TypedCreatorFunctionConsumer], classOfActor, ctor)

  /**
   * Scala API: create a Props given a class and its constructor arguments.
   */
  def apply(clazz: Class[_], args: Any*): Props = apply(defaultDeploy, clazz, args.toList)

  /**
   * Java API: create a Props given a class and its constructor arguments.
   */
  @varargs
  def create(clazz: Class[_], args: AnyRef*): Props = apply(defaultDeploy, clazz, args.toList)

  /**
   * Create new Props from the given [[akka.japi.Creator]].
   *
   * You can not use a Java 8 lambda with this method since the generated classes
   * don't carry enough type information.
   *
   * Use the Props.create(actorClass, creator) instead.
   */
  def create[T <: Actor](creator: Creator[T]): Props = {
    val cc = creator.getClass
    if ((cc.getEnclosingClass ne null) && (cc.getModifiers & Modifier.STATIC) == 0)
      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 coc = classOf[Creator[_]]
    val actorClass = Reflect.findMarker(cc, coc) match {
      case t: ParameterizedType ⇒
        t.getActualTypeArguments.head match {
          case c: Class[_] ⇒ c // since T <: Actor
          case v: TypeVariable[_] ⇒
            v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac ⇒ c } getOrElse ac
          case x ⇒ throw new IllegalArgumentException(s"unsupported type found in Creator argument [$x]")
        }
      case c: Class[_] if (c == coc) ⇒
        throw new IllegalArgumentException(s"erased Creator types are unsupported, use Props.create(actorClass, creator) instead")
    }
    apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
  }

  /**
   * Create new Props from the given [[akka.japi.Creator]] with the type set to the given actorClass.
   */
  def create[T <: Actor](actorClass: Class[T], creator: Creator[T]): Props = {
    apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
  }
}

/**
 * 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(classOf[MyActor], arg1, arg2)
 *
 *  val otherProps = props.withDispatcher("dispatcher-id")
 *  val otherProps = props.withDeploy(<deployment info>)
 * }}}
 *
 * Examples on Java API:
 * {{{
 *  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(2L)
final case class Props(deploy: Deploy, clazz: Class[_], args: immutable.Seq[Any]) {

  if (Modifier.isAbstract(clazz.getModifiers))
    throw new IllegalArgumentException(s"Actor class [${clazz.getName}] must not be abstract")

  // derived property, does not need to be serialized
  @transient
  private[this] var _producer: IndirectActorProducer = _

  // derived property, does not need to be serialized
  @transient
  private[this] var _cachedActorClass: Class[_ <: Actor] = _

  private[this] def producer: IndirectActorProducer = {
    if (_producer eq null)
      _producer = IndirectActorProducer(clazz, args)

    _producer
  }

  private[this] def cachedActorClass: Class[_ <: Actor] = {
    if (_cachedActorClass eq null)
      _cachedActorClass = producer.actorClass

    _cachedActorClass
  }

  // validate producer constructor signature; throws IllegalArgumentException if invalid
  producer

  /**
   * 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 mailbox information from the
   * contained [[Deploy]] instance.
   */
  def mailbox: String = deploy.mailbox match {
    case NoMailboxGiven ⇒ Mailboxes.DefaultMailboxId
    case x              ⇒ x
  }

  /**
   * Convenience method for extracting the router configuration from the
   * contained [[Deploy]] instance.
   */
  def routerConfig: RouterConfig = deploy.routerConfig

  /**
   * Returns a new Props with the specified dispatcher set.
   */
  def withDispatcher(d: String): Props = copy(deploy = deploy.copy(dispatcher = d))

  /**
   * Returns a new Props with the specified mailbox set.
   */
  def withMailbox(m: String): Props = copy(deploy = deploy.copy(mailbox = m))

  /**
   * Returns a new Props with the specified router config set.
   */
  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 withFallback deploy)

  /**
   * Obtain an upper-bound approximation of the actor class which is going to
   * be created by these Props. In other words, the actor factory 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] = cachedActorClass

  /**
   * INTERNAL API
   *
   * 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]].
   */
  private[akka] def newActor(): Actor = {
    producer.produce()
  }
}

/**
 * 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.
 */
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 actor’s instantiation, and
   * that the instance created for calling `actorClass` is not necessarily reused
   * later to produce the actor.
   */
  def actorClass: Class[_ <: Actor]
}

private[akka] object IndirectActorProducer {
  val CreatorFunctionConsumerClass = classOf[CreatorFunctionConsumer]
  val CreatorConsumerClass = classOf[CreatorConsumer]
  val TypedCreatorFunctionConsumerClass = classOf[TypedCreatorFunctionConsumer]

  def apply(clazz: Class[_], args: immutable.Seq[Any]): IndirectActorProducer = {
    if (classOf[IndirectActorProducer].isAssignableFrom(clazz)) {
      def get1stArg[T]: T = args.head.asInstanceOf[T]
      def get2ndArg[T]: T = args.tail.head.asInstanceOf[T]
      // The cost of doing reflection to create these for every props
      // is rather high, so we match on them and do new instead
      clazz match {
        case TypedCreatorFunctionConsumerClass ⇒
          new TypedCreatorFunctionConsumer(get1stArg, get2ndArg)
        case CreatorFunctionConsumerClass ⇒
          new CreatorFunctionConsumer(get1stArg)
        case CreatorConsumerClass ⇒
          new CreatorConsumer(get1stArg, get2ndArg)
        case _ ⇒
          Reflect.instantiate(clazz, args).asInstanceOf[IndirectActorProducer]
      }
    } else if (classOf[Actor].isAssignableFrom(clazz)) {
      if (args.isEmpty) new NoArgsReflectConstructor(clazz.asInstanceOf[Class[_ <: Actor]])
      else new ArgsReflectConstructor(clazz.asInstanceOf[Class[_ <: Actor]], args)
    } else throw new IllegalArgumentException(s"unknown actor creator [$clazz]")
  }
}

/**
 * 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(clazz: Class[_ <: Actor], creator: Creator[Actor]) extends IndirectActorProducer {
  override def actorClass = clazz
  override def produce() = creator.create()
}

/**
 * INTERNAL API
 */
private[akka] class TypedCreatorFunctionConsumer(clz: Class[_ <: Actor], creator: () ⇒ Actor) extends IndirectActorProducer {
  override def actorClass = clz
  override def produce() = creator()
}

/**
 * INTERNAL API
 */
private[akka] class ArgsReflectConstructor(clz: Class[_ <: Actor], args: immutable.Seq[Any]) extends IndirectActorProducer {
  private[this] val constructor: Constructor[_] = Reflect.findConstructor(clz, args)
  override def actorClass = clz
  override def produce() = Reflect.instantiate(constructor, args).asInstanceOf[Actor]
}

/**
 * INTERNAL API
 */
private[akka] class NoArgsReflectConstructor(clz: Class[_ <: Actor]) extends IndirectActorProducer {
  Reflect.findConstructor(clz, List.empty)
  override def actorClass = clz
  override def produce() = Reflect.instantiate(clz)
}

Other Akka source code examples

Here is a short list of links related to this Akka Props.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.