Class Models for Lua

# Component Models

The LOOP project extends the Lua programming language to support popular object-oriented programming features combined with adaptive and reflection mechanisms. Additionally, it also supports other programming paradigms related to the object concept like component-based development. Similar to the class models, the component model also provides dynamic features like reflection support.

## General Model

Similarly to the class model, the component model is also provided by a set of packages that present different features. Basically, the component model is implemented by two kinds of packages: component and port packages. A component package provides means to define and create components with different features and the port packages provides different implementations of ports used to link components together. In the examples below, we assume that a component package is loaded in variable `component` and a port package is loaded in variable `port`. The packages provided by LOOP that implement the model described in this section are listed below.

Component packages:
  - `loop.component.base`
  - `loop.component.wrapped`
  - `loop.component.contained`
  - `loop.component.dynamic`
Port packages:
  - `loop.component.base`
  - `loop.component.intercepted`

### Ports

The LOOP component model defines only two kinds of ports, called facets and receptacles. Each port is identified by a unique name on the component. Component systems are assembled connecting facets to receptacles.

A facet is a port that provides some functionality by means of an interface, i.e. values and methods. Facet represents the functionalities provided by the component. In LOOP, a facet is realized by an object that provides the values and methods of the facet. A component can have multiple facets and even facets providing the same service with different features, e.g. performance.

On the other hand, a receptacle is a port that requires some functionality through an interface. Receptacles defines the explicit dependencies of the component. A receptacle is realized as a table field that holds a reference to the object that provides the required functionality, i.e. the object connected to the receptacle. LOOP also defines the concept of multiple receptacles, i.e. receptacles that can hold multiple references simultaneously. Multiple receptacles are realized as a table containing the references to all objects connected to the receptacle. Multiple receptacles provides the following operations that may be used to access the objects connected.

`receptacle:__bind(object [, id])`
  Binds `bind` to the receptacle using the optional parameter `id` as the connection identifier and returns the connection identifier used.
`receptacle:__unbind(id)`
  Removes de connection identified by parameter `id`.
`receptacle:__hasany()`
  Returns a non-nil value if there is any object connected to the receptacle.
`receptacle:__get(id)`
  Returns the object connected to the receptacle with identifier `id`.
`receptacle:__all()`
  Returns an iterator that can be used in a `for` statement to iterate over all objects connected to the receptacle with the following construction
  `for conn_id, object in receptacle:__all() do ... end`

There are three different types of multiple receptacles, as described below.

`ListReceptacle`
  Accepts connections and automatically generates identifiers, therefore it ignores the identifier provided to `__bind` method.
`HashReceptacle`
  Accepts connections with application-defined identifiers, therefore it is necessary to provide an identifier to the `__bind` method.
`SetReceptacle`
  Accepts only one connection for each object and uses the object as the connection identifier, therefore it also ignores the identifier provided to `__bind` method.

### Templates

A component is a computational element that provides a set of ports that can be connected to other components or objects in order to assemble a computational system. The set of ports provided by a component is defined by a template. A component template is realized as an object that maps port names to a specific port type. The code below illustrates the definition of a component template.

    local DispatcherTemplate = component.Template{
      requester = port.Facet,
      scheduler = port.Receptacle,
      objectmap = port.HashReceptacle,
    }

Component templates are responsible to create all the infrastructure necessary for the execution of a component implementation.

### Factories

Once a component template is defined we can create factories of components that follow that template. For example, consider the following class that is used as the constructor of implementations of components of the template defined above.

    -- implementation
    Requester = oo.class{ context = false }
    function Requester:push(request)
      local object = self.context.objectmap[request.object_key]
      if object then
        local operation = object[request.operation] or
                          self.defaultops and self.defaultops[request.operation]
        if operation then
          self.context.scheduler:start(operation, object, unpack(request.params))
        end
      end
    end

    -- factory
    DispatcherFactory = DispatcherTemplate{ requester = Requester }

    -- component
    local dispatcher = DispatcherFactory()

To create a factory, we use a component template as the factory constructor. Such constructor gets as parameter the factory definition, i.e. a table containing the constructor of each facet defined by the template. The constructor of each facet must be a callable object (e.g. function or class) that returns a new implementation for the facet of each new component created by the factory. In the example above, we create a factory of components that follow the template `DispatcherTemplate` and each facet `requester` is an instance of class `Requester`.

Alternatively, a factory may also define a constructor for the component itself aside from the constructor of its facets. The component constructor is defined by index 1 or using the special name `__component` in the factory definition. If a constructor is provided for the component then all facets that do not have a specific constructor defined are implemented by the implementation returned by the component constructor. For example, we can create a factory of dispatcher components similar to the one described above using the following code:

    -- factory with component implementation that also implement all facets
    OtherDispatcherFactory = DispatcherTemplate{ Requester }

When a component is created, the component implementation's constructor is executed first if it is provided. This constructor receives as parameter all the values passed to the factory. After that, the constructor of each facet is executed. The facet constructors receive as parameter the field of the component implementation that matches the name of the facet plus a reference to this component implementation. Therefore, the component implementation may provide an initialization object to the provided the facet constructor. For example, the following code will create a component which the `requester` facet has the field `defaultops` defined.

    -- component
    local dispatcher = DispatcherFactory{
      requester = { -- object passed to class 'Requester'
        defaultops = {
          getid = function(self)
            return self.id
          end,
        }
      }
    }

A similar approach may be used to define initial values for the receptacles, like in the example below.

    -- component
    local dispatcher = DispatcherFactory{
      objectmap = _G, -- allow disptach to global objects.
    }

### Implementation

A single component instance may be made of different objects that are responsible to implement different facets. In addition to that, the component is also made of its receptacles that contains references to its dependencies. All this information if provided to the component by the context object. The context object is a table that maps the name of each port to its implementation, in case of facets, or to the objects connected in case of receptacles. Additionally, the context object also contains the factory that created the object and the component implementation at indexes `__factory` and `__component`, respectively.

The context object is provided to the component's implementation and each facet implementation by field `context`. If the implementation object provides the field context with a value different from `nil`, then this field is changed to hold the context object, like in the example of the section above.

Alternatively, if the field `context` is a function then this function is called as a method of the implementation object that receives the context object as a parameter. The invocation of the `context` function also signals that all implementation objects are created, so it is useful if some part of the component needs to perform some initialization only when the whole component's implementation is created. Since the order in which the implementation objects are created is not defined, the `context` method may also be used to initialize some state of the object that is dependable of the others component's implementation objects, like in the example below.

    -- facet 'requester' implementation
    Requester = oo.class()

    function Requester:context(context)
      self.context = context
      -- check if the 'defaultops' are defined in the component's implementation
      self.defaultops = self.defaultops or context.__component.defaultops
    end

### Assembly

Once components are created they shall be assembled by connection of their ports. Connections are typically established by setting a facet of a component to the receptacle of another. However, in case of multiple receptacles, this is done by operation `__bind` provided by this kind receptacles. The following example is extracted from the basic kernel architecture of the OiL ORB.

    -- CLIENT SIDE
    ClientBroker = component.Template{
      broker     = port.Facet,
      proxies    = port.Receptacle,
      references = port.Receptacle,
    }
    ObjectProxies = component.Template{
      proxies = port.Facet,
      invoker = port.Receptacle,
    }
    OperationInvoker = component.Template{
      invoker   = port.Facet,
      requester = port.Receptacle,
    }

    -- SERVER SIDE
    ServerBroker = component.Template{
      broker     = port.Facet,
      objects    = port.Receptacle,
      acceptor   = port.Receptacle,
      references = port.Receptacle,
    }
    RequestDispatcher = component.Template{
      objects    = port.Facet,
      dispatcher = port.Facet,
    }
    RequestReceiver = component.Template{
      acceptor   = port.Facet,
      dispatcher = port.Receptacle,
      listener   = port.Receptacle,
    }

    function assemble(components)
      setfenv(1, components) -- to avoid the 'components.' prefix.

      -- Client side
      OperationInvoker.requester = OperationRequester.requests
      ObjectProxies.invoker      = OperationInvoker.invoker
      ClientBroker.proxies       = ObjectProxies.proxies
      ClientBroker.references    = ObjectReferrer.references

      -- Server side
      RequestReceiver.listener   = RequestListener.listener
      RequestReceiver.dispatcher = RequestDispatcher.dispatcher
      ServerBroker.objects       = RequestDispatcher.objects
      ServerBroker.acceptor      = RequestReceiver.acceptor
      ServerBroker.references    = ObjectReferrer.references
    end

### Introspection

All LOOP component packages provide some introspection functions that are used to retrieve information about the structure of the components like the type of their ports or their implementations, like is listed below.

`factoryof(component)`
  Returns the factory object that was used to create the component `component`.
`templateof(component|factory)`
  Returns the template of a component or the template used by a component factory.
`ports(template)`
  Returns an iterator for all ports defined by a component template. The iteration variables get the name of the current port and the kind of the port that is one of the field of the port package like `port.Facet` or `port.HashReceptacle`.
`segmentof(component, portname)`
  Returns the implementation of the port with name `portname` of component `component`.

## Component Packages

The component package that implements the standard model is `loop.component.contained`. This package makes no assumption about the component's implementation objects, therefore these objects may be userdata or even non-indexable values like functions or threads. This is because the template provided by this package creates additional objects to represent the component's external interface, which is provided to clients to access the component ports, and also the context object, which is the component's internal interface. The figure bellow illustrates the schema of components created with templates of package `loop.component.contained`.

Alternatively, LOOP also provides other component packages that provide templates that avoid creation of additional objects for each component like `loop.component.wrapped` that creates only one additional object to represent the component external interface, but uses the component's implementation itself as the context object and therefore stores all ports values (facet implementation and receptacle connections) and the factory in the component's implementation object. This results that the component's implementation object can access all ports using the `self` parameter and avoid an additional table index to get the context object. The only advantage of providing an additional object for the external interface of the component is to be able to provide an alternative external view like is used by ports with support for interception like is described in section Interception.

The component package `loop.component.base` provides the lightest template implementation. Such templates use the component's object implementation both as the external and internal interface of the component, and therefore are very similar to ordinary objects, however they provide all the features described in the first section.

## Port Interception

Package `loop.component.intercepted` is a port package that provides implementation of component ports with support for interception. When a component template is created with ports from this package, interceptors may be registered in the component to be executed whenever an access is performed on its ports.

Interceptors may be defined for each component, or for all components created from a given factory, or even for all components of a given template. The interceptors are registered by the use of the following function provided by the `loop.component.intercepted` package.

`intercept(component|factory|template, portname, event [, interceptor])`
  Sets `interceptor` as the interceptor to be invoked when event `event` is triggered in port with name `portname` of the component or components defined by the first parameter. If the `interceptor` is omitted or is `nil` then the interceptor registered for that event is removed. The possible values for `event` are:
  - `"method"`: a port's method is invoked.
  - `"call"`: the port is invoked like a function.
  - `"index"`: a port's field is indexed (this event always precede the `"method"` event).
  - `"newindex"`: a port's field is set.

WARNING: There is a current limitation that intercepted ports cannot provide fields that contains function values. Such fields can only be methods, i.e. functions that are invoked immediately after they are indexed and take the `self` parameter.

### Interceptors

Interceptors are ordinary objects that may provide methods `before` and `after`. The semantics provided by these methods (the `self` parameter is implict) are described below.

`before(request, ...)`
  Receives the `request` table that contains the following fields:
  - `context`: context object of the component.
  - `port`: name of the port being intercepted.
  - `object`: implementation object of the facet or object connected to the receptacle.
  - `event`: kind of the event that triggered the interception (`"method"`, `"call"`, `"index"`, or `"newindex"`).
  - `method`: if `event` is `"method"` then this field is provided with the function that implements the method being invoked.
  - `field`: if `event` is `"index"` or `"newindex"` then this field is provided with the name of the field being accessed.
  In addition to the `request` table this method also receive additional parameters that differ depending on the event that triggered the interception. In case of `"method"` and `"call"` these parameters are the parameters of the call, including the `self` parameter in case of method invocations. In case of `"index"` these parameters are only the name of the indexed field. In case of `"newindex"` these parameters are the name of the indexed field and the new value being set to it.
  This method must always return the values that must be used as the parameters of the actual event being intercepted. Therefore, to allow that the event be performed with unchanged parameters the method must return the additional parameters provided (i.e. the expression `...`)
  Finally, this method may cancel the event so it is not delegated to the component implementation. This is done by definition of field `cancel` of table `request` provided as the first parameter of the method. In this case, the values returned by this method are used as the return values passed to the client of the event intercepted.
`after(request, ...)`
  Receives the same `request` table that was provided to the `before` method for the this triggered event. Therefore, if some value is stored in the `request` table in the execution of the `before` method, such value is available in this table as well. The additional parameters are the return values of the triggered event. In case of `"method"` and `"call"` these parameters are the return values of the call. In case of `"index"` these parameters are only the value of the indexed field. In case of `"newindex"` no additional parameters are provided.
  Similar to the `before` method, this method must always return the values that must be used as the return values of the actual event being intercepted. Therefore, to allow that the event be performed with unchanged return values the method must return the additional parameters provided (i.e. the expression `...`)

### Example

    ProfilerInterceptor = {}

    function ProfilerInterceptor:before(request, ...)
      if request.event == "index" then
        local field = ...
        self.lastindexed = field
      elseif request.event == "method" then
        -- should work because method index and invocation are atomic
        request.name = self.lastindexed
        request.start = os.time()
      end
      return ...
    end

    function ProfilerInterceptor:after(request, ...)
      if request.event == "method" then
        print(string.format("operation %s.%s(...) spent at least %d seconds.",
          request.port,
          request.name,
          os.difftime(os.time(), request.start)
        ))
      end
      return ...
    end

    local component = require "loop.component.base"
    local port      = require "loop.component.intercepted"

    local MyTemplate = component.Template{
      myfacet      = port.Facet,
      myreceptacle = port.Receptacle,
    }

    port.intercept(MyTemplate, "myfacet", "index", ProfilerInterceptor)
    port.intercept(MyTemplate, "myfacet", "method", ProfilerInterceptor)
    port.intercept(MyTemplate, "myreceptacle", "index", ProfilerInterceptor)
    port.intercept(MyTemplate, "myreceptacle", "method", ProfilerInterceptor)

## Dynamic Changes

Package `loop.component.dynamic` is a component package that provides templates that can be changed dynamically and reflect such changes on all its instances. This package provides operations to add and remove ports of all components of a given template or factory or also from a single instance. When a new facet is added, it is possible to define the implementation of that facet that will be instantiated for each component instance on demand following the same implementation protocol used for ordinary facet implementations. The functions provided by the `loop.component.dynamic` package are described below.

### Functions

`addport(component|factory|template, portname, portkind [, constructor])`
  Defines a dynamic port with name `portname` of the kind specified by parameter `portkind` that may be any port kind provided by packages `loop.component.base` or `loop.component.intercepted`. The parameter `constructor` is used in addition of facets to define the constructor of their implementation objects.
`removeport(component|factory|template, portname)`
  Removes a dynamic port that was added by function `addport` described above. It is not possible to remove ports that were not added dynamically.

### Example

    local component = require "loop.component.dynamic"
    local port      = require "loop.component.base"
    local iport     = require "loop.component.intercepted"

    local MyTemplate = component.Template()

    component.addport(MyTemplate, "inspector", port.Facet, oo.class{
      context = function(self, context)
        self.context = context
      end,
      getfieldof = function(self, port, field)
        self.context[port][field]
      end,
    })

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.