At the foundation of a ComponentX application is a set of components that are written in Java. These components process XML in the form of W3C Document Object Model (DOM) that is sent and recieved through an event model. Thus, all of Java's processing power can be used within these components. Java can by used to preform complex calculations, access data bases, network to remote servers, access the file system, wrap other Java applicaitons, and all other sorts of processing. ComponentX itself provides a base set of these components for commonly used processing of XML. This set can be readily extended with other native Java components as needed within an appliation.
To define a new native Cx Component, its Java source must:
For example, this is the source to the componentX Memory component:
package com.dat.cx.lib; import com.dat.cx.*; import com.dat.xset.*; import java.io.*; import java.util.*; import org.w3c.dom.*; public class CxMemory extends CxLibraryComponent { protected CxPin sendPin; protected CxPin clearPin; protected CxPin sentPin; protected CxPin clearedPin; protected CxXmlPort inputPort; protected CxXmlPort outputPort ; protected CxXmlPort errorPort; class Data implements Serializable { Node savedDoc = null; } protected Object createData() { return new Data(); } public CxMemory() { super(); sendPin = definePin( "send", CxPort.PROVIDES); clearPin = definePin( "clear", CxPort.PROVIDES); sentPin = definePin( "sent", CxPort.USES); clearedPin = definePin( "cleared", CxPort.USES); inputPort = definePort( "input", CxPort.PROVIDES); outputPort = definePort( "output", CxPort.USES); errorPort = definePort( "error", CxPort.USES); inputPort.addCxListener(new CxAdapter(this) { public void recieve( EventObject evt ) { ((CxMemory)toComp).processXml((CxXmlEvent) evt); } }); sendPin.addCxListener(new CxAdapter(this) { public void recieve( EventObject evt ) { ((CxMemory)toComp).send((CxEvent) evt); } }); clearPin.addCxListener(new CxAdapter(this) { public void recieve( EventObject evt ) { ((CxMemory)toComp).clear((CxEvent) evt); } }); } protected void processXml( CxXmlEvent xevt ) { XSet doc = new XSet(xevt.getNode()); if (doc.getSet("/cx:send") != null) { send(xevt); } else if (doc.getSet("/cx:clear") != null || doc.getSet("/cx:begin") != null) { clear(xevt); } else if (doc.getSet("/.").getName().startsWith("cx:")) { return; } else { Data data = (Data) getData(xevt); data.savedDoc = xevt.getNode(); } } void send(CxEvent evt) { Data data = (Data) getData(evt); if (data.savedDoc == null) postXml( errorPort, constructError( "No document saved"), evt ); else { postXml( outputPort, data.savedDoc, evt ); postEvent( sentPin, evt); } } void clear(CxEvent evt) { Data data = (Data) getData(evt); data.savedDoc = null; postEvent( clearedPin, evt ); } public String getIcon() { return "images/comp2Memory.gif"; } }
To declare a pin, port, or property use:
To create a pin, use the convenience method definePin that is inherited from CxLibraryComponent. DefinePin takes two arguments, the name of the pin and its direction. Use CxPort.PROVIDES or CxPort.USES for specifying the direction.
To create a port, use the convenience method definePort that is inherited from CxLibraryComponent. DefinePort takes two arguments, the name of the port and its direction. Use CxPort.PROVIDES or CxPort.USES for specifying the direction.
To create a property, use the convenience method defineProperty that is inherited from CxLibraryComponent. DefineProperty takes two arguments, the name of the property and its initial value.
The definePin, definePort, and defineProperty calls should be defined in the constructors of the native component to insure correct initialization.
Note: the definePin, definePort, and defineProperty calls should be used instead of directly creating a CxPin, CxPort, or CxProperty with the java new statement. These calls will properly initilize the pin, port, or property so that any instances of the new component will be correctly saved in a componentX project or import file.
For all input pins and ports (i.e. have a direction of CxPort.PROVIDES) an event listener needs to be registered in order for the pin or port to become active.
To register and event listener for a pin or port, use the addCxListener method of the pin or port. AddCxListener takes a single argument an object that implements the interface CxListener. This can be the component itself, providing the component implements CxListener, or an adapter object. In either case the object needs to implement the receive() method of the CxListener interace.
The receive method has a single argument a java.util.EventObject. If the receive method is for a listener registered on a pin, the EventObject can be safely cast to a CxEvent. If the receive method is for a listner registered on a port, the EventObject can be safely cast to a CxXmlEvent.
Once a listener has been registered on a pin, the receive method will be called whenever a signal is sent to that pin. A cast of ObjectEvent to CxEvent may be needed to access persistent data as discussed in section Handling Persistent Data. The recieve method may perform any desired processing.
Once a listener has been registered on a port, the receive method will be called whenever a document is sent to that port. A cast of ObjectEvent to CxXmlEvent is needed access the document. Once cast the getNode() method will return the W3C DOM node of the document. A cast of ObjectEvent to CxXmlEvent is also needed to access persistent data as discussed in section Handling Persistent Data. The recieve method may perform any desired processing.
Typically an adapter is used as the registered listener on a pin or a port where the adapter simply has a recieve method that calls a processing method within the component. As a convenience a CxAdapter class is provided as a default listener. When registering an event listener on a pin or port, simply create a new instance of CxAdapter with the new statement and for that instance override the recieve method to call the desired method within the component The constructor of CxApdater takes a single argument that should be the component itself (i.e. this). The recieve method can then cast CxAdapter's toComp variable to be the class of the component and thus the method of the component can be called.
Within a processing method a signal can be sent on an output pin useing the inherited method postSignal. The postSignal method takes two arguments, the pin on which to send a signal and a source event. The source event must be the event orignially recieved by the receieve method. This is needed to correctly propagate context information.
A document and be sent to any output port using the inherited postXML method. The postXML method takes three arguments, the port on which to send the document, the document, and a source event. The document must be a W3C DOM Node. This does not need to be the root of a documents DOM tree. It can be any DOM node desired to be sent and be considered to be the root of the document. The source event must be the event orignially recieved by the receieve method. This is needed to correctly propagate context information.
As a convenience in processing a DOM tree, componentX includes the utility class XSet. XSet provides rapid navigation, creation, and manipulation of the DOM tree. For further information on XSet see the com.dat.xset package in the ComponentX API documentation.
The processing methods can use the values defined in the component's properties to control the processing behavior of the component. To access the value of a component use the property's getValue() method. The getValue method returns a Java String containing the current property value.
A CxProperty implements the java.beans.PropertyChangeListener interface. Thus a component can register a property change listener on the property.
If the component is to retain data between events, this data needs to be placed in a separate object that is managed by the componentX architecture. This is needed to insure that different instances of the the components or separate threads of execution within a running componentX system do not interfere with each other.
The separate object may be any Java object that is serializable.
If the component has persistent data, then it must override the createData method of CxLibraryComponent. The createData override method must create and return an instance of the data object in which data will be stored.
With a createData method in place, the processing methods of the component initial create and subsequently access a correct data object for the component instance and thread of execution using the inherited method getData. GetData takes a single argument, a source event. This must be the event of the recieve method cast to either a CxEvent or a CxXmlEvent. GetData will return the data object. This should be cast to the class of the data object.
To compile a native, include CxStudio.jar found in the lib directory of the ComponentX installation directory.
To install a native component into a ComponentX project, first run CxStudio insuring that class path includes the path to the .class file of the new native component. Then use the import java component from the file menu to add the native component to a ComponentX project. Finally save the project file.
Note: the path the all .class file of the native components used in a project must be included when running CxStudio in order to successfully open the project file.
For further information the programming a native component, see the ComponentX API documuentation.