ECT Component Design/Implementation Guidelines/Notes

Chris Greenhalgh 2005-04-22

General

Each ECT component is contained in (currently) its own JAR file, along with any component-specific supporting classes and a BeanInfo class. This JAR file ("capability") is normally deployed via the download/java/components directory. Any supporting JAR files are DLLs deployed via the download/java/common directory.

BeanInfo

Currently, every ECT component must have its own BeanInfo class (i.e. a class which implements the java.beans.BeanInfo interface) and whose name (including package) is the same as that of the main component class followed by BeanInfo. E.g. the component class foo.bar.MyComponent has an accompanying Bean Info class foo.bar.MyComponentBeanInfo in the same JAR file. This requirement is enforced by equip.ect.ContainerManagerHelper which looks inside component JAR files and determines which class (currently only the first one found) is the main component class to be instantiated for that capability. [in future, properties in the manifest might be used to avoid the need for explicit BeanInfo classes and/or multiple components might be allowed in a single JAR]

BeanDescriptor

Every Bean's BeanInfo provides a Bean descriptor (java.beans.BeanDescriptor) which can include:
It is suggested that for ECT:

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...

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.

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).

Property Types

Most 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 1D 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.

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).

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.