ECT Component Design/Implementation Guidelines/Notes

Created by Chris Greenhalgh 2005-04-22
Last update by Stefan Rennick Egglestone 2005-11-03

General

ECT components are deployed into the ECT Java container in the form of beans which have been packaged in jar files. Each component available through a container is defined by a component class, and any number of supporting classes. To methods are available to allow ECT to recognize which classes are beans - either a developer must either provide a BeanInfo class per component which is packaged into the same jar, or they must specify this information through a manifest file included in the jar. This JAR file is normally deployed via the download/java/components directory. Any supporting JAR files or DLLs are deployed via the download/java/commondirectory.

Once components have been deployed into a container, they may be advertised in a capability browser ( equip.ect.apps.editor.grapheditor.CapabilityBrowser) , which is capable of making requests for instance of a component from a container, and instances of components may be manipulated in the graph editor ( equip.ect.apps.editor.grapheditor.GraphEditor).

BeanInfo specification method

BeanInfo classes are those which implement the java.beans.BeanInfo interface) and whose name (including package) is the same as that of the main component class with which they are associated, followed by the string BeanInfo. For example, a component defined by a component class called foo.bar.MyComponent would have an accompanying Bean Info class foo.bar.MyComponentBeanInfo in the same JAR file. Information is extracted from BeanInfo classes in ECT by class equip.ect.ContainerManagerHelper.

BeanDescriptor

Classes which implement the BeanInfo interface must provide a method which can return a java.beans.BeanDescriptor) object which describes the bean. Information included in the bean descriptor can include:
In ECT:

Additionally, the htmlDescription attribute can be used to specify any length documentation necessary for a component. See file Documenting components for more information on the use of this attribute.

PropertyDescriptors

Each component property is described by a Property Descriptor (java.beans.PropertyDescriptor). This identifies the properties "external" name, its set and get methods (normally both, or just a get method for read-only properties), the property's type (java class, normally automatically determined) and the same additional information as for a Bean Description, above. It is suggested that the same convention is used.

Icon

A Bean Info can provide an icon for its bean, in one or more combinations of 16x16 or 32x32 and mono or colour. 16x16 colour is recommended as a minimum, with a transparent background.

BeanInfo Implementation

Fully Implemented BeanInfo

Up to now most/all components have implemented a BeanInfo class by extending the java convenience class java.beans.SimpleBeanInfo and overriding (usually only) the getPropertyDescriptors method in order to describe its properties with their setters and getters. So far most components have not provided the additional information identified above.

Introspected BeanInfo

Currently a BeanInfo class must exist to allow the component to be detected and exported. However, if the provided BeanInfo class returns null (not a zero sized array) from any of its methods then reflection will be used to determine the component's properties, etc. using the normal java bean idiom (e.g. "public T getN()", "public void setN(T)" for properties). While this avoids having to implement a (tedious) BeanInfo class, it does not allow additional human-oriented information to be provided (see above).

Javadoc-generated BeanInfo

There is now a custom Java Doclet which will generate a BeanInfo file from the corresponding Bean .java file, using the standard method-naming convention and incorporating additional information from a subset of the Bean's Javadoc comments. See the target "beaninfotest" in build.xml to see how to do this (in that case for the Template bean). Currently the new BeanInfo file will be generated in the top-level directory, and it is suggested that this is copied to the component directory and checked in there. The Javadoc entries and tags used are:

Future possibility: File-configured BeanInfo

How about using an XML file to create the BeanInfo...

Note that the Introspector will share BeanInfo classes, so each component will need to have its own unique BeanInfo class. However, this could just be a trivial (no code) extension of a common class, which based on its own name would read a resource and create appropriate Descriptors...

Furthermore, the resource could be autogenerated by a custom Doclet (or similar?!) processing the component source, so that the same information is included in the Javadocs for the component...

Manifest-based specification method

An alternative to providing a BeanInfo class for every component in a jar is to provide a manifest with the jar. This can be used to specify which classes in the jar represent components, and additional information about these components.

The manifest if present is typically called manifest.mf, and must be specified in the component's ant jar task. The manifest - if present - must identify all components in the jar, and can also specify the classification(s) of those components (see BeanInfo, attribute "classification", above). An example manifest is:

Manifest-Version: 1.0 

Name: equip/ect/components/phidgets/PhidgetInterfaceKit.class
Java-Bean: True
classification: Hardware/Input & Output

The lower section ("Name:..." onwards) is repeated for each component in the jar, with a blank line between each such section.

Both a short description and a longer html description can be specified in the manifest, and ECT uses these attributes in the same way as the shortDescription property and htmlDescription attribute that can be defined in a bean info class. For more information, see file Documenting components

Life-cycle and Persistence

Creation

All bean must have a public no-args constructor, which will be used to create instances. This should leave the bean in a usable but unconfigured state.

Destruction

If the bean makes use of resources that need to be released (e.g. COM ports), then it must implement a method with signature "public void stop()" which will be called by the container when the component is being stopped/destroyed, and which should release all resources.

Note that external problems, e.g. termporary loss of remote services, may cause the "same" component instance to be stopped and subsequently recreated within the same container. Therefore it is important that - if at all possible - stopping a component does return all resources to a state where a similar component can be created anew.

Configuration

In general a component should be prepared to accept changes to its input (writable) properties in any order and at any time. However this is quite difficult with some types of components, e.g. those interfacing to particular hardware devices. In this case a two-phase life-cycle model is provided, with the phases:
  1. unconfigured - as created, ready to be configured (e.g. to have a COM port and baud rate specified).
  2. configured (also known as running) - all configuration information has been provided and the component is now in a distinct active state; further configuration may now be impossible.

While unconfigured the various configuration-specific parameters should be set (and settable). Once fully configured the property "configured" (if present) should be set to true. The component should now respond to any other (non-configuration) properties.

NB to allow the container persistence and configuration manager to correctly initialise such components it is required that all properties that must be set before the component is fully configured begin with the string prefix "config". If the component needs to know when it has been configured then it should provide a binary property called "configured". This will be set after the other "config..." properties and before any other writeable properties.

ECT-aware components

If a component wishes to interact directly with ECT or the installation dataspace, or to be aware of property link requests applied to it, then it can implement the interface equip.ect.IActiveComponent. When created the initialise method will be calling, giving the component a reference to the container installation dataspace client and the container manager.

Additional methods are also called when the component is linked to or is about to be updated because of a link. This is used e.g. in the dictionaryarraymerge component to merge incoming values (this may become default behaviour for array-type properties in the future).

Events

At present, only property change events are supported by the container.

Note that the container relies on the name and value in the property change events to track changes to properties being set. Therefore the editor (and other external components) will only "see" the change made to a property if a corresponding property change event is fired by the component in each set method, with the correct property name (as specified in the component's BeanInfo, NOT its actual method name).

Properties

Property Types

Simple types

Simple properties should have simple types. In particular, the system has a number of standard (best effort) type coercions, e.g. between all java primitive types, and also strings and arrays of any of these. These types should be used wherever possible for simplicity and interoperability. E.g. the graphical editor views and sets all property values as strings, and the component's container uses this coercion to get a value of the appropriate target type. Note: arrays are stringified in the form "{el1,el2,el3}". By default string values are quoted within stringified arrays.

It is now also possible to directly use subclasses of equip.runtime.ValueBase as property types (the root serializable type in EQUIP); these will be used directly.

Structured types

There is now also a general-purpose structured type, equip.data.DictionaryImpl. This should be used to communicate structured information and information with associated metadata. This is equivalent to a hash table with keys restricted to being strings and value beings subtypes of equip.runtime.ValueBase. equip.ect.Coerce.toClass(Object,Class) can be used to coerce to/from ValueBase types. The default coercion to/from simple types maps from/to the property named "value". Java Serializable types should also be converted to/from DictionaryImpl types by introspection (untested :-). A Dictionary can also be coerced to/from a java.util.Hashtable. Note: dictionaries are stringified in the form "{prop1=value1,prop2=value2}". A dictionary with no properties is stringified as "{=}" (inspired by Lingo property lists :-) By default string values are quoted within stringified dictionaries.

Use of dictionary type is supported by the components dictionarymerge - which combines two dictionary-type values into one (merging properties) - and dictionaryarraymerge - which combines any number of arrays of dictionary-type values into one.

Note that dictionary types are also used with the dataspaceinterface component, which allows linked values to be exposed directly as tuples and retrieved via the usual dataspace pattern matching.

Sub-component types

If a property's type is a 1D array of a Serializable class then the container will normally assume that these are sub-components of the parent component, and will expose each element of the array to the infrastructure as a sub-component with its own properties, etc. It will add a synthetic "parent" property identifying the parent component. A sub-component should also have a property of type string called "persistentChild", which is used a key for sub-component persistence. I.e. if a parent component is recreated by the persistence mechanism, then the new child components will be matched to equivalent old child components based on having the same value for their "persistentChild" property. Therefore this must be consistent and meaningful (e.g. a X10 subcomponent corresponding to a particular lamp module should always have a consistent persistentChild property value, typically the X10 address of that particular module).

Note: at some point in the future this may change to require a particular interface to be implemented by sub-components.

Local interace types

Other properties which are not Serializable are currently assumed to direct references to internal Java objects that should be passed directly by reference between components. Currently, this will not work between components in different JVMs. This is used by the video and audio framework components to allow video consuming components to get rapid access to individual video frames via a shared object without each operation invocation being routed via the dataspace. Note that this also means that such invocations are not logged and cannot be replayed.

Dynamic Properties

Components which implement the interface equip.ect.DynamicProperties (typically implemented using equip.ect.DynamicPropertiesSupport) can present dynamically (run-time) generated properties to the framework. See for example the DynamicBeanShell component. The types can be as for ordinary properties - see above.

ECT-aware property setting

As already noted, a component that implements the interface equip.ect.IActiveComponent. will receive additional notifications of the creation and deletion of property link requests (currently only TO its properties), and can handle associated property value sets taking account of the links requests causing them. See for example the dictionaryarraymerge component.