Edgewall Software

Changes between Version 19 and Version 20 of TracDev/ComponentArchitecture


Ignore:
Timestamp:
Nov 21, 2010, 5:48:05 PM (13 years ago)
Author:
Sebastian Krysmanski <sebastian@…>
Comment:

Added a section about component lifecycle and component states

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/ComponentArchitecture

    v19 v20  
    118118Here, the `TodoList` class declares an extension point called `observers` with the interface `ITodoObserver`. The interface defines the ''contract'' that extending components need to conform to.
    119119
    120 The `TodoList` component notifies the observers inside the `add()` method by iterating over `self.observers` and calling the `todo_added()` method for each. This works because the `observers` attribute is a [http://users.rcn.com/python/download/Descriptor.htm descriptor]: When it is accessed, it finds all ''enabled'' components that declare to extend the extension point. For each of those components, it gets the instance from the component manager, potentially activating it if it is getting accessed for the first time.
    121 
    122 ''Note:'' Only component classes that are enabled (in the `[components]` section of [wiki:TracIni trac.ini]) will be available when iterating over an extension point (like in the code above). Thus it's ''not enough'' to only instantiate a component (e.g. by calling the component constructor as `ComponentName(self.compmgr)`) but one needs to enable it as well.
     120The `TodoList` component notifies the observers inside the `add()` method by iterating over `self.observers` and calling the `todo_added()` method for each. This works because the `observers` attribute is a [http://users.rcn.com/python/download/Descriptor.htm descriptor]: When it is accessed, it finds all ''enabled'' components (see [#component_lifecycle below]) that declare to extend the extension point. For each of those components, it gets the instance from the component manager, potentially activating it if it is getting accessed for the first time.
    123121
    124122== Plugging in to an extension point ==
     
    168166This output obviously comes from the `TodoPrinter`. Note however that the code snippet above doesn't even mention that class. All that is needed to have it participating in the action is to declare the class. ''(That implies that an extending class needs to be imported by a python script to be registered. The aspect of loading components is however separate from the extension mechanism itself.)''
    169167
     168== Component Lifecycle and State == #component_lifecycle
     169This section will shed some light on how components come to be. It describes some inner workings and terminology that you may come across when trying to understand components, and provides information about where the specific parts are implemented.
     170
     171First of all, let's repeat the basic rules for components:
     172
     173  '''Components are singletons. They should be stateless.'''
     174
     175This means each component only exists once (within a single process). It's designed to be reused for multiple - possibly concurrent - web request. So it can't (read: should not) store information from one request in its class members and reuse this information in the next request.
     176
     177Now, the following list describes the stages a component goes through until it's ready to be used:
     178
     179 1. ''Registration:'' The first step is to register a component so that Trac knows about it. This happens automatically when the Python file contain the component gets imported for the first time. This is done either with a `import` statement in a file that has already been imported, or - like in most cases - by listing the file in `entry_points` section of a plugin (see [wiki:TracDev/PluginDevelopment#Packagingplugins Packaging Plugins]).[[BR]]
     180    The registration is handled by `trac.core.ComponentMeta` using [http://www.ibm.com/developerworks/linux/library/l-pymeta.html metaclass programming].
     181 1. ''Activation:'' A component gets activated when it's first used. So, "activation" is basically just another word for "instantiation", though since a component is a singleton it's only activate/instantiated once. When a component gets activated, Trac's component manager adds some useful fields to the component (specifically: `env`, `log`, and `config`).[[BR]]
     182    A component can be activated by one of the following ways:
     183      * when its manually constructed for the first time (using `Component(compmngr)`)
     184      * when one of the extension points the component implements is used for the first time. Note that the component must be enabled for this way to work (see below).
     185   
     186 The activation and making sure that only one instance of a component exists is handled by `trac.core.Component.__new__()`. This method then calls `trac.core.ComponentManager.component_activated()`. In Trac the component manager is usually `trac.env.Environment`.
     187
     188As stated above components can be either "enabled" or "disabled". The main (and only) difference between these states is:
     189
     190  '''Extension points will ''only'' use enabled components.'''
     191
     192This means that the extension point methods (like `todo_added` in the example above) of a ''disabled'' component (that implements a certain extension point interface) ''won't be called''. Note also that even disabled components can be activated (instantiated), but only by constructing them manually (as mentioned before).
     193
     194Enabling a component is done in the `[components]` section of [wiki:TracIni trac.ini]. This is implemented in `trac.env.Environment.is_component_enabled()`. Whether a component is enabled or disabled is check ''only once'' when an extension point that component implements is first used.
     195
    170196== How components are used in Trac's code ==
    171197