Component types

Immediate component

Immediate component instances are activated immediately as soon as all references are satisfied.

A component is an immediate component if it defined the property "immediate": true and is not a factory component. A component is treated as immediate if it does not declare a "provides" property.

Immediate Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"],
  "immediate": true
}

If the component has no references, you can consider the lifetime of the component as equal to BundleActivator, which is created on the start of a bundle and destroyed during the stop of the bundle. Only immediate components are allowed to act asynchronously in their activation method. This means that a component instance is allowed to return a dojo.Deferred during its activate() method and the App Runtime waits until activate() finishes before it registers the component instance as a service at the App Runtime.

Delayed component

An instance of a delayed component is not activated until its service is first requested.

A component description which declares a "provides" property and which is not an immediate component and not a factory component is treated as delayed. This means that delayed components are the most common kind of components, because this is the default case if no other special property is declared.

Delayed Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"]
}

Delayed components are so named because of the fact that service registration is made before the component instance is created. This means that the creation of the component instance is delayed until the first use of the registered service. A delayed component instance is destroyed if it becomes unused. As a result, delayed components can have a very dynamic life because construction and destruction might happen very often and are highly dependent on usage of the service provided by the component.

If a delayed component declares the property "serviceFactory": true, the App Runtime creates a new service instance for each requesting bundle.

Factory component

An instance of a factory component provides a service that generates new component descriptions. This service allows client bundles to create and activate multiple component configurations and dispose of them.

A component description which specifies the property "componentFactory": true is treated as factory component.

Factory Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"],
  "componentFactory" : true
}

A factory component is a special design pattern, because it acts more as a template for the dynamic creation of component configurations based on component descriptions. The App Runtime parses the component description. If it is satisfied, it registers a service with the interface ct.framework.api.ComponentFactory for this configuration. This service provides a method newInstance, which can be used to create new component configurations during runtime. This newInstance method returns a ComponentInstance object, which provides the method getInstance for accessing the real instance, and the method dispose for shutting down the component configuration. The caller of newInstance is responsible for the disposal of the created component configuration.

Usage of a Factory Component
// Activator using a component factory
export default class{
  start(bundleContext) {
    // search special component factory with name "MyComponentFactory"
    let references = bundleContext.getServiceReferences("ct.framework.api.ComponentFactory","(Component-Name=MyComponent)");
    try {
      // get the component factory service
      let factory = bundleContext.getService(references[0]);

      // create a new component configuration
      // here the default component properties defined in the declaration of the component factory can be overwritten.
      let componentInstance = factory.newInstance({ "aproperty": "newValue" });

      // get component/service implementation
      // this feature should rarely be used, better define a reference
      let impl = componentInstance.getInstance();

      impl.doSomething();

      // the created component configuration has to be cleaned up later
      componentInstance.dispose();

    } finally {
      bundleContext.ungetService(references[0]);
    }
  }
}

Factory Components are a very powerful feature because the newInstance method creates new component configurations. This means that such a new dynamically created component configuration is managed by the App Runtime as if it were a new and independent component description in the manifest.json file. Therefore if the property provides is specified, the new component instance is registered as a service in the App Runtime.

Because of the ComponentInstance.getInstance method, a component configuration created by a factory component is treated as an immediate component, therefore, regarding its using state, the component instances are directly created. An important point is that a factory component is satisfied if all references of the component description are also satisfied. In some circumstances this definition leads to unexpected behaviors especially if references are using placeholders in filter expressions.

Usage of a Factory Component in other Component
// Other component using a component factory

// manifest.json
{
   "name" : "OtherComponent",
   "references" : [{
        "name" : "_factory",
        "providing" : "ct.framework.api.ComponentFactory",
        "filter": "(Component-Name=MyComponent)"
    }]
}

// implementation of OtherComponent
export default class {
  activate() {
      // create a new component configuration
      // here the default component properties defined in the declaration of the component factory can be overwritten.
      let componentInstance = this._factory.newInstance({ "aproperty": "newValue" });

      // get component/service implementation
      // this feature should rarely be used, better define a reference
      let impl = componentInstance.getInstance();

      impl.doSomething();

      // the created component configuration has to be cleaned up later
      componentInstance.dispose();
  }
}