How to Add a New Component to the EQUIP Component Toolkit

Chris Greenhalgh, 2004-06-03. v.3 2005-04-28

Building ECT

  1. Check out EQUIP4J from CVS (:pserver:anonymous@dumas.mrl.nott.ac.uk:/mrl/src/cvsroot path /Equator/equip4j
  2. Set up your environment for Java and ANT (set JAVA_HOME, ANT_HOME, add java and ant bin dirs to PATH).
  3. Build the infrastructure - cd Equator/equip4j/infrastructure; ant
You might want to jump ahead to Running ECT with your component to check that it is working.

Adding a Component

See also ECT_Component_Guidelines.html
  1. Make a new directory for your component under Equator/equip4j/infrastructure/src/equip/ect/components
  2. Write you Component in that directory as a Java Bean with full support for PropertyChangeListeners (consider using java.beans.PropertyChangeSupport, and copying the relevant delegation code from one of the other components; insert firePropertyChange calls in all property setters). Apparently Vetoable properties do not work in ECT at present (2004-06-03). You can add as many other supported classes in that directory as you need.
  3. Create a java.beans.BeanInfo class for your bean; its class name must be the same as your component bean with 'BeanInfo' appended.
  4. Edit the ant build file, Equator/equip4j/infrastructure/build.xml; copy the target for an existing component (e.g. the Chat component), and change the names of the target, jar and path to match your new component, e.g.
      <!-- FOO COMPONENT TARGET -->
    <target name="foo" depends="init, ectCore">
    <!-- compile and jar the component -->
    <antcall target="buildcomponent">
    <param name="compdir" value="foo"/>
    <param name="compclass" value="Foo"/>
    </antcall>

    <!-- dependencies...
    copy jars to ${dist-common}, jars with javax classes to ${dist-common-ext},
    DLLs to ${dist-common} -->

    </target>
  5. Also add you new target to the depends part of the all target at the bottom of the file (comma separated list).
  6. Build again: cd Equator/equip4j/infrastructure; ant. It should all compile :-)

Running ECT with your component

Try it out - using the webstart version - double click in "ect.jnlp" in the webstart directory, or the "runWebstart.bat" script. See ECT_Webstart_User_Guide.html for more details.
Alternatively, use the individual scripts in Equator/equip4j/infrastructure/install. If using scripts on a single machine then you will need to:
  1. run the batch file 'runEquipDataSpace.bat' - to start a dataspace, by default on port 9123 (equip://localhost:9123) and multicast discoverable using group 'equip.ect.default'.
  2. run the batch file 'runExporterGUI.bat' - to start a Java container. This should show your new component JAR along with the other component jars. At present you will need to activate these individually - select your component jar and click 'export capabilities'.
  3. run the batch file 'runGraphEditor.bat' - to start a graphical component editor. This has two main windows, 'Capability Browser' and 'Graph Component Editor'.
However you got started, you will now need to:
  1. In the Component Browser window expand the 'Capability Root' node and to find per-container sub-node(s); expand one of these to reveal the Capability for your new Component class.
  2. Right click on this Capability and select "Create Request" from the popup menu. This should create a ComponentRequest in the dataspace, which the container will see and instantiate a new instance of your component class. Check the container's console for diagnostic output from your component if nothing happens ("Help" menu, "Show Console").
  3. (You can delete the ComponentRequest by expanding your component's Capability node to reveal the created ComponentRequest sub-node,  right clicking on it and selecting "Delete request" from the popup menu.)
  4. In the Graph Component Editor window you should see a rounded box representing your component in the upper "Components" panel.
  5. Select this and then drag it down onto the lower "Editor" panel. This will now reveal your component's properties. The values should change to reflect the underlying component (if they do not then you have probably not supported the PropertyChangeListener correctly).
  6. Try setting a property of your component - right click on the property and select "Set value" from the popup menu; type a new value into the dialog and press "Set". The editor will ask the component to set that property to the specified value.
  7. Make some more components...
  8. To connect two component properties together, in the Graph Component Editor's Editor panel click and drag from the source (origin) property to the destination (target) property. This will create a PropertyLinkRequest which the destination property's container should implement, calling the destination property's setter when the source property's value changes.
  9. If you open the editor's optional "Property Link Browser" window ("View" menu) and select a linked property in the Editor view then an entries should appear in the in/out table to show the other end(s) of any links applying to that property.
  10. You can delete a link by right clicking on it in the Editor panel and selecting "Delete", or by selecting it in the Property Link Browser table and choosing 'Delete selected links' from the popup menu there.

More Advanced...

Making components with sub-components

Normal ("top-level") components are instantiated in response to explicit requests. On some situations components should be instantiated dynamically by the system, e.g. components which exclusively proxy hardware devices (e.g. camera, smart it, phidget) should be instantiated when the correponding hardware is detected. The recommended way to do this is via sub-components...
See for example the subcomponenttest and smartitfactory components.

Making components persistent

The dataspace will persist Component Requests and Property Link Requests. The container will also persist a components visible property values, and try to re-set them when it is created. The configuration manager allows components to be recreated in a similar way. Whereever possible it is recommended that you make use of this form of persistence, i.e. make the component's visible and settable properties sufficient to recreate the component. See ECT_Component_Guidelines.html for additional guidance on component life-cycle and configuration property naming.

However, if this is not possible for some reason (e.g. much too much state to expose as properties) then it is down to individual components to persist their own property values and/or internal state. The way to do this at present is:

At present the container persists all components at a regular interval. At some point this should be extended with consideration to (a) not persisting too often (b) components being able to request persisting (e.g. after a configuration change).

Note that components that persist themselve will not currently be recreated correctly by the configuration manager, since it does not have access to these container- and component-specific persistence files.

Making components remotely configurable

Components can be remotely configured in two main ways:
  1. By exposing input properties for configuration, which can be set from other component properties via links. Advantage: configuration is visible in the dataspace, and can be modified within the component system. Disadvantage: relatively slow and restricted to strings and simple types that have a coercion from string supported in ect.
  2. By exposing an embedded configuration web server, exposing the URL to this server via a property. E.g. see the httpconfigtextvalue component.
These should normally be combined, so that web server configuration is reflected in properties and vice versa.
The visionframework beans illustrate a short-cut of exposing a single configuration property which may be parsed to extract more complex configuration. The disadvantage of this is a more complex configuration language, and relatively help/feedback in using it.
At some point another ServiceUI approach could also be considered.

Making components that directly invoke methods on each other

This is currently only supported for components within the same JVM. The component providing the methods to be called must provide a readonly property whose value is an object which is NOT Serializable and which implements the interface in question. The component which will be calling these methods provides a readwrite property of the same type.
When the user makes Property Link Request FROM the interface provider TO the interface caller then a string key will be passed via the dataspace, but a (weak) reference to the actual underlying Java object will be set as the property value (provided, as noted, it is in the same JVM). The link destination component will then be able to invoke methods on the interface implemented by the source component.
See for example the visionframework, which pass video frames in this way (via a callback handler to allow sources to split).