Subsystems - laforge49/Asynchronous-Functional-Programming GitHub Wiki

Dependency Injection can be a great simplifier but as the complexity of an application grows, having all services in a flat namespace can make things difficult. There is a real need for subsystems and hierarchical dependency injection is a great way to implement that. We define a subsystem then as a system services actor which has a superior actor.

object Subsystem {
  def apply(systemServices: Actor,
            rootComponentFactory: ComponentFactory,
            mailbox: Mailbox = null,
            factoryId: FactoryId = new FactoryId("System"),
            properties: Properties = null,
            actorClass: Class[_ <: Id_Actor] = classOf[Id_Actor],
            actorId: ActorId = null) = {
    val subSystemFactory = new CompositeFactory(factoryId, rootComponentFactory, actorClass)
    SetProperties(subSystemFactory, properties)
    val _mailbox = if (mailbox == null) systemServices.mailbox else mailbox
    val subSystem = subSystemFactory.newActor(_mailbox).asInstanceOf[Id_Actor]
    if (actorId != null) {
      subSystem.id(actorId)
    }
    subSystem.setSystemServices(subSystem)
    subSystem.setSuperior(systemServices)
    subSystem._open
    subSystem
  }
}

Note that when a subsystem is created without a mailbox, it uses the mailbox of its superior.

A subsystem actor implements IdActor, so it can be registered with an ActorRegistry. Generally, it is a good idea for subsystems to be registered with their superior.

The FactoryRegistry and ActorRegistry services also support subspaces. So when an actor attempts to create an actor using a registered factory or to get a registered actor and the registration has not been done in a subsystem, the FactoryRegistry or ActorRegistry of the superior system services actor is accessed.

Here is a test where an actor tied to a subsystem uses a factory registered to the superior system services actor.

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
}

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

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

case class DoIt()

class Driver extends Actor {
  bind(classOf[DoIt], doit)
  setMailbox(new ReactorMailbox)

  def doit(msg: AnyRef, rf: Any => Unit) {
    systemServices(Instantiate(FactoryId("greeter"), null)) {rsp =>
      val greeter = rsp.asInstanceOf[Actor]
      greeter(Greet())(rf)
    }
  }
}

class SubsystemTest extends SpecificationWithJUnit {
  "SubsystemTest" should {
    "instantiate" in {
      val systemServices = SystemServices(new SomeComponentFactory)
      val aSubsystem = Subsystem(systemServices, new FactoryRegistryComponentFactory)
      val driver = new Driver
      driver.setSystemServices(aSubsystem)
      Future(driver, DoIt())
    }
  }
}

Output.

Hello world!

SubsystemTest

Tutorial