-
Notifications
You must be signed in to change notification settings - Fork 1
FactoryRegistry
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!