Skip to content
laforge49 edited this page Dec 9, 2011 · 7 revisions

Factories are used to instantiate and configure actors. It would also be nice to use factories when deserializing an actor, to avoid having to serialize any configuration data. And rather than serialize the factory class name, we can use a (likely much shorter) factory id. For this we use the FactoryId class. (See Name.)

FactoryId is the argument passed to the constructor of Factory. But it can be null, which is what we have done in earlier examples when subclassing Factory. (See Factory.)

The FactoryRegistry holds a (concurrent) mapping of factory id's to Factory objects and can be included as part of the root system services or as part of a subsystem object. This component supports two types of messages:

  1. Instantiate(FactoryId, Mailbox), which instantiates an actor and assigns the given mailbox. The new actor is also assigned the same system services as the requesting actor.
  2. Factories(), which returns a sequence over the map of factory id's to Factory objects.

Now of course, for this to work there must be some means of registering factories. This is done by calling FactoryRegistryComponentFactory.registerFactory(Factory).

Here is a simple actor and its factory that we can use to test the factory registry:

case class Greet()

class Greeter
  extends Actor {
  bind(classOf[Greet], greet)

  def greet(msg: AnyRef, rf: Any => Unit) {
    println("Hello world!")
    rf(null)
  }
}

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

Now we still need a means of configuring the factory registry component factory, which is to say we need to be able to call FactoryRegistryComponentFactory at some point. The way we do this is to create another component factory that will be a part of the process of building the root system services object and which (1) adds a dependency on FactoryRegistryComponentFactory so that the FactoryRegistryComponent is included in the root system services object and (2) adds the factory mapping for GreeterFactory. We can add the mapping by overriding the ComponentFactory.configure method:

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

  override def configure(compositeFactory: Factory) {
    FactoryRegistryComponentFactory.register(compositeFactory, new GreeterFactory)
  }
}

In the configure method above, we use the FactoryRegistryComponentFactory companion object to register the GreeterFactory class. (The configure method is called by Factory when a ComponentFactory object is included.)

Here is our test driver, which uses the Instantiate message to create an instance of the Greeter actor and then sends the Greet message to it:

case class DoIt()

class Driver extends Actor {
  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)
    }
  }
}

And then here is the test code:

val systemServices = SystemServices(new SomeComponentFactory)
try {
  val driver = new Driver
  driver.setExchangeMessenger(systemServices.newSyncMailbox)
  Future(driver, DoIt())
} finally {
  systemServices.close
}

FactoryRegistryTest

FactoryRegistry

Clone this wiki locally