Skip to content
laforge49 edited this page Oct 28, 2011 · 18 revisions

Instantiating an Actor from a FactoryId is an important service, and can be quite helpful when deserializing a persistent actor or when processing a remote request. The instantiate service is invoked by passing an Instanciate message to systemServices.

case class Instantiate(factoryId: FactoryId, mailbox: Mailbox)

systemServices(Instantiate(factoryId, mailbox) {rsp => ...}

Actor instantiation is always done synchronously by the SafeInstantiate class, which also handles dependency injection.

class SafeInstantiate(factoryRegistryComponentFactory: FactoryRegistryComponentFactory)
  extends Safe {
  def func(target: Actor, msg: AnyRef, rf: Any => Unit)(implicit sender: ActiveActor) {
    val factoryId = msg.asInstanceOf[Instantiate].factoryId
    val factory = factoryRegistryComponentFactory.getFactory(factoryId)
    if (factory != null) {
      val mailbox = msg.asInstanceOf[Instantiate].mailbox
      val actor = factory.newActor(mailbox)
      actor.setSystemServices(target)
      rf(actor)
      return
    }
    val superior = target.superior
    if (superior == null) throw new IllegalArgumentException("Unknown factory id: "+factoryId.value)
    superior(msg)(rf)
  }
}

SafeInstantiate is bound to the FactoryRegistryComponent.

class FactoryRegistryComponent(actor: Actor)
  extends Component(actor, componentFactory) {

  override def setComponentFactory(componentFactory: FactoryRegistryComponentFactory) {
    super.setComponentFactory(componentFactory)
    val cf = componentFactory.asInstanceOf[FactoryRegistryComponentFactory]
    bindSafe(classOf[Instantiate], new SafeInstantiate(cf, actor))
  }

  override def open {
    val cf = componentFactory.asInstanceOf[FactoryRegistryComponentFactory]
    val factories = cf.factories
    val it = factories.keySet.iterator
    while (it.hasNext) {
      val factory = factories.get(it.next)
      factory.configure(actor,cf)
    }
  }
}

Registered factories can override the configure(systemServices: Actor, factoryRegistryComponentFactory: FactoryRegistryComponentFactory) to validate the presence of a system service by calling systemServices.requiredService(reqClass: Class[_ <: AnyRef]) or to access other registered factories by calling factoryRegistryComponentFactory.getFactory(factoryId: FactoryId).

The FactoryRegistryComponent is, in turn, created by the FactoryRegistryComponentFactory, which handles the registration of factories.

class FactoryRegistryComponentFactory extends ComponentFactory {
  val factories = new java.util.TreeMap[String, Factory]

  def registerFactory(factory: Factory) {factories.put(factory.id.value, factory)}

  def getFactory(id: FactoryId) = {
    val factory = factories.get(id.value)
    if (factory == null) throw new IllegalArgumentException("Unknown factory id: " + id.value)
    factory
  }

  override def instantiate(actor: Actor) = new FactoryRegistryComponent(actor)
}

Other component factories call the registerFactory method from their configure method, which is called immediately after their dependencies have been satisfied. An example will help illustrate this.

Let's say we have an actor, Greeter, which prints a message when it receives a Greet message.

case class Greet()

class Greeter
  extends Actor {
  bind(classOf[Greet], greet)
  def greet(msg: AnyRef, rf: Any => Unit) {
    println("Hello world!")
    rf(null)
  }
}

Greeter is created by the GreeterFactory.

class GreeterFactory
  extends Factory(new FactoryId("greeter")) {
  override def instantiate = new Greeter
}

The ComponentFactory, SomeComponentFactory, registers the GreeterFactory.

class SomeComponentFactory
  extends ComponentFactory {
  addDependency(classOf[FactoryRegistryComponentFactory])

  override def configure(compositeFactory: Factory) {
    val factoryRegistryComponentFactory =
      compositeFactory.componentFactory(classOf[FactoryRegistryComponentFactory]).
        asInstanceOf[FactoryRegistryComponentFactory]
    factoryRegistryComponentFactory.registerFactory(new GreeterFactory)
  }
}

The dependency in SomeComponentFactory on FactoryRegistryComponentFactory serves two purposes here: (1) It ensures that the FactoryRegistryComponent is part of the composite and (2) It ensures that the FactoryRegistryComponentFactory is accessible before the call to configure.

To test this, we need a Driver actor, as Future can not be used directly when a Safe object is used. When Driver receives a DoIt message, it creates a Greeter actor using the Instantiate service and then pass it a Greet message.

case class DoIt()

class Driver extends Actor {
  setMailbox(new ReactorMailbox)
  bind(classOf[DoIt], doit)
  def doit(msg: AnyRef, rf: Any => Unit) {
    systemServices(Instantiate(FactoryId("greeter"), null)) {rsp =>
      val greeter = rsp.asInstanceOf[Actor]
      greeter(Greet())(rf)
    }
  }
}

What remains then is to create the system services composite, create the Driver actor, inject the system services into the Driver actor and then pass a DoIt message to Driver.

val systemServices = SystemServices(new SomeComponentFactory)
val driver = new Driver
driver.setSystemServices(systemServices)
Future(driver, DoIt())

Output.

Hello world!

FactoryRegistryTest

Tutorial

Clone this wiki locally