All TDI Connectors implement the "com.ibm.di.connector.ConnectorInterface" Java™ interface. This interface provides a number
of methods to implement addressing all the possible ways of using
a Connector within TDI. Usually the Connectors you write
will not require all the options provided by TDI and
we will actually need to implement only a subset of the methods presented
in the "ConnectorInterface" interface. It is the "com.ibm.di.connector.Connector"
class that makes this possible.
"com.ibm.di.connector.Connector" is an abstract class
implementing "ConnectorInterface" that contains core Connector
functionality (for example processing of Connector's configuration)
and also provides empty or default implementation to many of the methods
from "ConnectorInterface". This allows us to start implementing
your Connector by subclassing "com.ibm.di.connector.Connector"
and focusing on (implementing) only those methods from "ConnectorInterface"
that provide value in your case, and that are actually necessary for
your Connector.
Listed below are the "ConnectorInterface" methods that
build the backbone of a real Connector, and which we will usually
need to implement:
In the constructor
we will usually set the name of the Connector (using the "setName(...)"
method) and define what modes - Iterator, Lookup, AddOnly, Server, Delta etc. - that your Connector supports (using the
"setModes(...)" methods). For an example of a Connector
implementation, look at the "DirectoryConnector.java"
Connector included in this package.
Usually the "initialize(...)"
method reads the Connector's parameters and makes the necessary preparations
for the actual work (creates a connection, etc.) based on the parameter
values specified.
Place
in "selectEntries(...)" any code we need to execute
prior to actually starting to iterate over the Entries. When the Connector
operates on a database, that code could be an SQL SELECT query
that returns a result set; when the Connector operates on an LDAP
directory, that code could be a search operation that returns a result
set. The result of the "selectEntries(...)" (result set, etc.) is later used by the "getNextEntry(...)" method
to return a single Entry on each call/AssemblyLine iteration. Of course
you might not need any preparation to iterate over the Entries (as
in the case with the FileSystem Connector) in which case there is
no need to implement "selectEntries(...)". By subclassing
"com.ibm.di.connector.Connector" we will inherit its default
implementation that does nothing.
It
is expected to return a single Entry that feeds the rest of the AssemblyLine.
There
are no general guidelines for implementing this method - it
all depends on the information this Connector is supposed to access.
This method retrieves data from the connected data source and must
create an Entry object and populate it with Attributes. For example, a database Connector would read the next record from a table/result
set and build an Entry object whose Attributes correspond to the record's
fields.
This method finds matching data in the connected system
based on the "Link Criteria" specified in the Config Editor GUI. For
example, a database Connector would execute a SELECT query
with the appropriate WHERE clause based on Link Criteria
and then build an Entry from the database record, in the same way
as "getNextEntry()" does. Please consult the Java Docs for the structure of the SearchCriteria
input parameter.
Use the following implementation pattern to achieve the
above required Connector behavior: for each Entry found call Connector's
"addFindEntry(...)" method. When finished, call "getFindEntryCount(...)"
to get the number of Entries we have found - if it is 1, return
the value returned by "getFirstFindEntry(...)" , otherwise
return NULL.
For example: In a database Connector, "modEntry(...)"
executes an SQL UPDATE query, using the Attributes of the
entry parameter as database fields and the SearchCriteria in the search
parameter to build the WHERE clause.
The goal of this method is to add/save/store the Entry
object (passed in as parameter to this method) into the Connector's
data source. So, a database Connector would execute an INSERT
SQL statement using the Entry's Attributes' names and values
and table fields names and values.
Before discussing the "modEntry(...)"
method, a short clarification of the Update mode is necessary: When
the AssemblyLine encounters a Connector in Update mode, it will first
execute Connector's "findEntry(...)" method using the
specified Link Criteria. If "findEntry(...)" finds no
matching Entry, then the Connector's "putEntry(...)"
method is called to add the Entry to the data source. If "findEntry(...)"
finds exactly one Entry, the Connector's "modEntry(...)"
method is called. Finally, if the "findEntry(...)" method
finds more than one Entry, the "On Multiple Entries" hook is executed
and depending on what the user specified either no Connector's calls
are invoked or one of "putEntry(...)", alternatively
"modEntry(...)" methods is invoked.
As seen above
there are two variants of the "modEntry(...)" method -
one with three and one with two input parameters. The two parameters
that you get in both cases are: entry, the output mapped conn Entry, ready to be written to the data source; and search, the SearchCriteria
to be used to make the modify call to the underlying system. When
this method is invoked by the Update mode logic (the "update(...)" method
of an AssemblyLineComponent), this will reference the actual SearchCriteria
built from the Link Criteria (after evaluation of Attribute values, etc.).
The extra parameter is old. This is the original
Entry in the data source as it looks right now, before the modification
is applied. This information might be useful in certain cases like
"rename" operations when we need the old name to perform the rename.
It
is up to you to decide which of these methods to use. Of course you
could implement both of them. One of them is sufficient for your Connector
to support Update mode.
Following the analogy with the database
Connector, "modEntry(...)" would execute an SQL UPDATE query, using the Attributes of the entry parameter as database fields and
the data from the search parameter to build the WHERE clause
of the SQL query.
This mode
is appropriate when your Connector participates in some kind of request-response
communication. The output mapped entry parameter contains
the data necessary to perform the "call" or "request" part of the
operation. For example, the Web Service Connector builds and transmits
a SOAP call based on the Attributes in entry. The method
then must build and return an Entry object from the reply/response
data.
Delete mode will cause the Connector
to perform a "findEntry(...)" to try and locate the Entry
to be deleted. If the "findEntry(...)" method returns
exactly one Entry, the "deleteEntry(...)" method is called
with this Entry and the Link Criteria used in the Lookup as parameters.
If "findEntry(...)" returns zero or more than one Entries
the corresponding Connector hooks are called. Depending on what the
user specified in the script code, either nothing more is executed
or the "deleteEntry(...)" method is called with the Entry
specified by the user script via the AssemblyLineComponent method setCurrent(
entry ). Unless the current entry is set in the On Multiple Found
hook, nothing more happens, and control passes down the AssemblyLine.
Back
to our database Connector example, "deleteEntry()" would
execute an SQL DELETE statement.
Since Connectors in Server mode
handle client requests which require a response, the AssemblyLine
will call the "replyEntry(...)" Connector method at
the end of the AssemblyLine. Use this method to place your code that
returns response to the client. In case your Connector might need
to return multiple responses on a single request we can code the
"putEntry(...)" method so that it returns an individual
response Entry. In this case it will be the responsibility of the
AssemblyLine developer to call the "putEntry(...)"
method of the Connector by scripting and this fact has to be documented
in the Connector's documentation.
When implementing a Connector
in Server mode, you also have to take care about terminating the Connector
on external request. Place your termination code in the "terminateServer()"
method. Take into consideration that this method can be called on
the master Connector instance that accepts client requests and also
on a child Connector instance processing a client request. In both
cases proper termination should happen: it is usually a good termination
practice to stop accepting new requests from the master Server Connector
instance but let all child Connectors finish their processing. The
"terminateServer()" method usually sets some flag that is
checked by the "getNextClient()" method of the master Server
Connector instance - if termination is requested the "getNextClient()"
method will return NULL. This is a signal to the AssemblyLine that
this Server Connector has terminated and the AssemblyLine will not
call anymore its "getNextClient()" method.
The methods listed above are the core ConnectorInterface methods
that bring life to our Connector. And remember, you only need to
implement the methods corresponding to the Connector modes that your
Connector will support.
Modes to methods mapping
When you write a connector we should take into account that users
may call the connector methods in no particular order. This means
we should have sanity checks on each method in case the connector
requires certain methods to be called before others. From a TDI server
perspective the methods called on a connector is determined by the
value of the mode parameter. In this section we will see the call
order for methods for each mode. When the AssemblyLine uses a connector
it always calls initialize() as the first method before
any other methods are called. However, it is possible that the user
sneaks in a call to other methods before this is called.
How to implement a Delta mode Connector
First of all, to enable delta mode for your connector
add "Delta" to the list of supported modes. Delta mode is a bit special
since it can be emulated by the AssemblyLine or directly implemented
by the connector. Emulated delta mode simply means that the incremental
update is generated by the AssemblyLine based on what it is returned
by the findEntry() method and what is being written to
the target system. If the target system supports incremental updates
we can code your connector by translating a delta Entry object to
the underlying protocol of the connector. In the latter case you
configure your connector by returning true in the isDeltaSupported() method.
This will cause the AssemblyLine to forward the delta Entry directly
to our connector's putEntry(), modEntry() or deleteEntry() methods, bypassing findEntry() and the algorithm to compute the
delta entry.
Query Schema behavior
In TDI 7.1 a default behavior for schema discovery is implemented
for all Connectors. This default behavior is used by Connectors that
do not implement their own logic of schema processing, that is, do
not override the querySchema(Object) method. The default
behavior depends on the Parsers that a Connector has (if any).
Under a static
schema we understand the schema that is configured in the tdi.xml file for the Connectors and Parsers.
To add a static schema definition to our Connector, Parser or Function
definition file, add a <Schema> tag inside the <Connector>, <Parser>
or <Function> element. The name of the Schema should be "input"
or "Output".
For example:
Implementing querySchema()
ConnectorInterface also provides other methods that address
aspects of the possible use of a Connector and which you might want
to implement. One example is "querySchema(...)". This
method returns the schema of the connected data source . If you implement
it, the Config Editor presents the returned values as the Connector's
Schema.
These return values are stored as a Vector of Entry objects, one
for each column/attribute in the schema. For example, a database Connector
would return one Entry for each column in the connected database table.
Each Entry in the Vector returned should contain the following
attributes:
Specified by: querySchema in
ConnectorInterface
Parameters: source - The object
on which to discover schema. Usually NULL. This may be an Entry or
a string value.
Returns: A vector of com.ibm.di.entry.Entry objects
describing each entity, or in the case of error, a java.lang.Exception
is thrown.
If your connector extends the base implementation of the TDI connector
(com.ibm.di.connectors.Connector), we can invoke the initParser() method
to initialize the associated parser:
You
have to provide the input and/or output streams the parser will use
for its read/write operations. The mode of the connector typically
determines which way the flow goes (note that your initialize(Object
obj) connector method will have the connector mode in the "obj"
object). You are not required to initialize the parser at the time
of connector initialization, but we should do so unless there is
a good reason to initialize it elsewhere. In any case we should invoke
the initParser() method to properly initialize the parser
with logging objects, debug flags and other standard TDI objects/behaviors.
The parser can be chosen either by the user or we can hide the
parser selection and either provide the configuration in your "tdi.xml"
file or programmatically configure the parser in your connector (or
both).
In this
case set the parameter "parserOption" in your connector's
"tdi.xml" file to the value "true".
Once this field is defined the selection of the parser is delegated
to the user through a standard user interface (note that we can prefill
the parserConfig section of the tdi.xml file
with a default parser). Here is a snippet from the FileSystem connector's
"tdi.xml" file showing "parserOption" as "Required", which means the
connector requires a parser (that is, an error is thrown if none is
defined in the configuration):
The value for the "parserOption" parameter can
be "Required", "Useless" (no parser allowed) or "Optional".
You
can include the parserConfig section in your "tdi.xml" file if you
always use the same parser, for example if you inherit from the CSV
Parser:
Your
connector has access to the ConnectorConfig object via the Connector.getConfiguration() method.
Through the ConnectorConfig object we can obtain the ParserConfig
interface object for the connector. Use that object to configure the
parser before you invoke the initParser() method:
Once the parser has been initialized
we can invoke the readEntry() and writeEntry() methods
to translate com.ibm.di.entry.Entry objects to and from the stream
format defined by the parser. You typically invoke the readEntry() method
in your getNextEntry() method and the writeEntry method from
your putEntry method. You obtain the parser interface handle through
the getParser() method.
If
your connector can function with or without a parser we can invoke
the hasParser() method to determine whether a parser is configured
or not:
If we use multiple instances of the parser
during the life time of the connector we should close the parser
interface to ensure data is written to the outputstream and that system
resources are released. The methods used to re-initialize a
parser can differ based on which parser we use but the following
method calls should be sufficient for most parsers:
When your connector is terminated
it will automatically invoke the closeParser() method if
one is in use by the connector. The com.ibm.di.connector.Connector class that your Connector
will be extending, has a number of methods to enable you to log messages
to the AssemblyLine's configured log files. The simplest way of
logging is using one of the following methods:
We can call these methods with
code like
This will
cause your string to be issued to the AssemblyLine's configured
log appenders, at INFO level. If we want to do more advanced logging, the com.ibm.di.connector.Connector class also has this field:
This com.ibm.di.server.Log class
has many methods for logging. We could therefore use the myLog object
to do logging like this:
This
issues a message to the log(s) at ERROR level. There are corresponding
methods for logging at different levels, like loginfo() and logfatal().
When building the source code of the Connector, set up your CLASSPATH to include the jar files from the
"jars/common" folder of the IBM TDI installation.
At minimum you would need to include "miserver.jar"
and "miconfig.jar".
When integrating your Java code with IBM TDI, pay attention
to the collection of pre-existing components that comprise IBM TDI, notably in the jars directory. If your code relies upon one
of our own library components that overlap or clash with one or more
that are part of the TDI installation there will most
likely be loader problems during execution.
When we create a custom TDI component you also have to provide
an additional file that describes your component to TDI. This file
is located at the root of the jar file and is named tdi.xml.
The syntax and contents of this file is described in this document.
The first part of this section explains the format of the tdi.xml
file and also shows the minimum requirements for a component definition
file.
The second part of this section focuses on the form definition
and the various options we have when you define a form. This form
definition is used by the TDI Configuration Editor to let the user
configure your component. While the UI options in the form definition
are basic and somewhat limited, we can still perform advanced operations
using our own custom java based UI components as well as associating
scripts with form events.
The files are created in XML format looking much the same as a TDI Configuration
file.
A skeleton for the file could look something like this:
This defines a Connector named system:/Connectors/CustomConnector.
The Java class that implements this Connector is com.acme.CustumerConnector.class.
Localization of labels and descriptions in this file can be provided
by adding properties files with the locale identifier
in the standard way. In this example, the properties file is CustomConnector.properties.
Then the German version of this file would be CustomConnecter_de.properties, and the Brazilian Portuguese version would be CustomConnector_pt_BR.properties.
The individual properties in these localized files take the same keys, but with localised values. Each line is of the format
Comments
in these files can be included by starting the line with a # (hash).
When you first create your component definition file you add the
main sections for the components your jar file contains. For each
component you add a section where you as a minimum define Java class.
The syntax is as follows for the three main components:
In addition we should always include a form definition for
each of the components. This is to prevent the configuration editor
to report errors of missing forms. If your component has no configurable
parameters we should include a form that says so.
The
current configuration object that the Form refers
to is always the connectorConfig/parserConfig/functionConfig object.
If we need to access the main component's parameters we should use
the "config.getParent()" method to obtain
for example the ConnectorConfig interface for the configuration.
When starting either the configuration editor or the server there
is a component called the TDI Loader that runs through its configured
jar directories looking for *.jar/*.zip files that contain an "tdi.xml"
file at its root level. All the definitions in these files are put
into the system namespace.
The locations of these files are:
When you put your jar file in either of these directories your
component will show up in the configuration editor with the name you
chose as part of the system namespace.
Adding your
jar file to the CLASSPATH or PATH alone does not include it in the
system templates and hence will not be visible to the user.
The form description is used to provide custom input panels for
components. While most of the user interface in the configuration
editor is static, most components need specific user interfaces to
let the user define its behavior.
The form definition defines the input fields and labels that the
configuration editor will build when we open the configuration for
a component. The binding between the component (for example, connector, parser) and its form is through the Java class of the component. Using
the example above, the connectorConfig has a "connectorType" parameter
that defines the implementing class for the component (com.acme.CustomConnector).
When a component of this type is presented to the user, the configuration
editor will look for a form with the same name as the implementing
Java class.
When the form has been created it also has a binding object for
each parameter to the configuration object. These binding objects
will set the initial value of the input field (using the default value
provided by the form if the configuration object returns null for
the value) and also function as the controller between the input field
and the configuration object. When the input field changes its value
the binding will update the configuration object and vice versa. The
configuration object is read and updated using the primitives of the
configuration object (for example, BaseConfiguration.getParameter/setParameter).
It is possible to have the binding object invoke specific methods
rather than using the primitives, but for component developers this
is rarely needed.
Forms are defined the same way as components are defined. Below
is an example of a form with three input fields and one event handler
trapping changes to one of the parameters. The form definition is
divided into two sections; General and Advanced. The General section
contains two parameters ("firstParameter" and "$GLOBAL.debug"), whereas the second section contains just one parameter ("secondParameter").
We have only defined a label for the two parameters; $GLOBAL.debug
is a TDI global parameter that enables detailed logging when checked.
The translation file (CustomConnector_en.properties)
would contain:
A FormSection element contains a list of FormSections or FormItems.
This list has the tag <FormSectionNames>; the FormSection can optionally
include a title and redefinitions of FormItems. These FormItems inherit
from the FormItem in the Form that the FormSection is part of. This
allows us to, for example, override the Tooltip for that FormItem.
The Form contains a list of FormSections; this list is tagged <FormSectionNames>.
In any list of FormSections, the word $Mode will be replaced by the
current mode for the Connector. This allows us to show parameters
depending on the mode of the Connector.
Here is a somewhat complex example of a complete form:
Definition of XML Tags:
Instead of merging the translated values from the properties file
into the XML file, there is a new tag in the Form, <TranslationFile>.
The correct local version of this translation file will be read in
when using the Config Editor, and the values will then be used.
Example for the File Connector: the tdi.xml file for the File Connector
contains this tag for the Form:
You
would package this XML file with all the NLS/idi_conn_filesys.properties
files in the jar file.
These are the recognized parameters that can be used in a FormItem.
script Script2 allows for
a second button to the right of the first one.
scriptLabel scriptHelp The values list can contain static and
dynamic values. The dynamic values are expanded and added to the array
at runtime to populate the dropdown list.
The syntax parameter for a FormItem can have any of the following
values:
If you modify this parameter via script or java code make
sure to invoke BaseConfiguration.setProtectedParameter()
instead of BaseConfiguration.setParameter().
The setProtectedParameter will automatically create a new property
if there isn't one in place already. If the password store is not
configured setProtectedParameter will simply invoke setParameter instead.
Droplist
syntax:component Version 7.x - Eclipse SWT Components
The class is instantiated by FormWidget2 at runtime and should
be an SWT Control subclass (something that can be a child of a Composite).
Also, it must have a constructor as shown in this example:
This component will be placed in a Composite using GridLayout.
Do not set the GridData of the custom UI object as this is done by
the form widget after creating the custom class.
In your form definitions we can add calls to script functions.
These functions execute in the form's script engine. The form's script
engine provides the following predefined objects:
Look at TDI's components in the configuration editor to find
an example you find suitable. Use a zip/jar tool (for example, winzip, unzip) and extract the "tdi.xml" file from the component's
jar file (TDI_install_dir/jars/components subdirectory).
Also, the examples/connector_java folder
of this package contains the "tdi.xml"
file of the Directory Connector.
In order to take advantage of the TDI Reconnect feature, the Connector's
.xml file may contain rules that tailor the Connector's response
to interruptions in connectivity. These rules are in addition to any
built-in rules of the Reconnect engine of the TDI Server.1
The rules for the particular Connector appear in the "connectors"
section as a sibling of the "connectorConfig" sub-section like this:
Each rule has the following parameters:
Parameters "exceptionClass" and "exceptionMessageRegExp" are optional -
if not specified, the rule will match all exception classes and all
exception messages respectively.
For a detailed description of the regular expression syntax used
in "exceptionMessageRegExp", please see the the JavaDoc of the java.util.regex.Pattern class
at http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Pattern.html.
Example
Now that we have the Connector source code compiled and supplied
the "tdi.xml" file, we are ready to package
and deploy the Connector.
What we need to do is create a jar file (typically with the same
name as that of the Connector) and include in it:
After creating the jar file of the new Connector, we need
only drop that jar file in the "jars/connectors"
folder of the IBM TDI installation. The next time the system starts
up, it will automatically load the new Connector and make it ready
for use.
1.
In
order for TDI to remain backwards-compatible, there are two
built-in rules that emulate previous version's behavior.
Unless reconnect is switched off entirely for a Component, a reconnect
will be attempted for all Exceptions of type java.io.IOException and javax.naming.CommunicationException.
Table 85.
Mode
Methods
Comments
Iterator
Initialize()
selectEntries()
getNextEntry()
terminate()
getNextEntry should return NULL to signal end
of input.
AddOnly
Initialize()
putEntry()
terminate()
Lookup
nitialize()
findEntry()
terminate()
If you find more than one entry we should use clearFindEntries() and addFindEntry() for
each entry found in this method.
Delete
Initialize()
findEntry()
deleteEntry()
terminate()
Update
Initialize()
findEntry()
putEntry()
modEntry()
terminate()
If findEntry returns a single entry, modEntry will
be called. If findEntry returns null, putEntry will be
called.
Delta
Initialize()
findEntry()
putEntry()
modEntry()
deleteEntry()
terminate()
See note below.
<Connector name="ibmdi.Mailbox"> <Schema name="Input"> <SchemaItem>
<Name>mail.body</Name>
<Syntax>javax.mail.Multipart</Syntax>
</SchemaItem>
</Schema>
<Schema name="Output"> <SchemaItem>
<Name>Flag.Answered</Name>
<Syntax>boolean</Syntax>
</SchemaItem>
</Schema>
</Connector>
name
The name of the attribute (column, field, etc.)
Required.
syntax
The syntax (like VARCHAR or TIMESTAMP)
or expected value type of this attribute. Optional
Using a Parser in your Connector
/**
* Initialize the connector's parser with input and output streams. If the parser
* has not been loaded then an attempt is made to load it. The input and output objects
* may be Stream objects (InputStream,OutputStream), java.io.Reader object, String object, * java.net.Socket, byte and character array objects.
*
* @param is The input object.
* @param os the output object.
* @exception Any exception thrown by the parser
* @see #getParser
*/
public void initParser (Object is, Object os) throws Exception;
<Connector name="ibmdi.FileSystem"> <Configuration>
...
<parameter name="parserOption">Required</parameter>
</Configuration>
</Connector>
<Connector name="myconnector"> <Configuration>
<parameter name="parserOption">Required</parameter>
</Configuration>
<Parser>
<InheritFrom>system:/Parsers/ibmdi.CSV</InheritFrom>
... Optional parameter values to make the parser functional
</Parser>
</Connector>
import com.ibm.di.config.interfaces.ConnectorConfig;
public void initialize(Object obj) throws Exception {
// Check mode
String mode = "" + obj;
boolean isIterator = mode.equals(ConnectorConfig.ITERATOR_MODE);
ConnectorConfig cc = (ConnectorConfig)getConfiguration();
// Get the parser config object
ParserConfig parser = cc.getParserConfig();
// -- use the csv parser and set the column separator parameter
parser.setParameter("parserType", "com.ibm.di.parser.CSVParser");
parser.setParameter("csvColumnSeparator", "\t");
if(isIterator)
initParser(inputStream, null);
else
initParser(null, outputStream);
if(hasParser())
doSomething();
// Close parser to release system resources
if(getParser() != null)
getParser().closeParser();
// assuming we just got a new input stream ... reinitialize the parser
initParser(inputStream, null);
Logging from a Connector
/**
* Log a message to the connector's log. The message is prefixed by the connector's
* name.
*
* @param msg The message to write to the log
*/
public void logmsg(String msg)
/**
* Log a debug message to the connector's log
*
* @param msg The message to write to the log
*/
public void debug(String msg)
logmsg("initializing my connector");
/**
* The log object for logging messages
*/
protected com.ibm.di.server.Log myLog;
myLog.logerror("Something very bad happened");
Building the Connector's source code
Implementing the Connector's GUI configuration form
Introduction
tdi.xml file format
<?xml version="1.0" encoding="UTF-8"> <MetamergeConfig version="7.0"> <Folder name="Connectors"> <Connector name="CustomConnector"> <Configuration>
<parameter name="connectorType">com.acme.CustomConnector</parameter>
... more parameters ...
</Configuration>
</Connector>
</Folder>
<Folder name="Forms"> <Form name="com.acme.CustomConnector"> <TranslationFile>CustomConnector</TranslationFile>
... many more elements which will be defined later ...
</Form>
</Folder>
</MetamergeConfig>
key=value
Basic Component Definitions
Table 86.
Component Type
Minimum Section Contents
Connector
<Folder name="Connectors"> <Connector name="your_name"> <Configuration>
<parameter name="connectorType">your_javaclass_name</parameter>
</Configuration>
</Connector>
</Folder>
Parser
<Folder name="Parsers"> <Parser name="your_name"> <parameter name="class">your_javaclass_name</parameter>
</Parser>
</Folder>
Function
<Folder name="Functions"> <Function name="your_name"> <Configuration>
<parameter name="javaclass">your_javaclass_name</parameter>
</Configuration>
</Function>
</Folder>
Install Location
Form description
Component/Form Association
Form/Configuration Binding
Form Definition
<Folder name="Forms"> <Form name="com.acme.CustomConnector"> <TranslationFile>CustomConnector</TranslationFile>
<parameter name="title">title_key</parameter>
<parameter name="formevents">function firstParameter_changed() { form.alert("First param modified"); }</parameter>
<FormSectionNames>
<ListItem>General</ListItem>
<ListItem>Advanced</ListItem>
</FormSectionNames>
<FormSection name="General"> <FormSectionNames>
<ListItem>firstParameter</ListItem>
<ListItem>$GLOBAL.debug</ListItem>
</FormSectionNames>
</FormSection>
<Formsection name="Advanced"> <parameter name="title">Advanced_Title</parameter>
<parameter name="initiallyExpanded">false</parameter>
<FormSectionNames>
<ListItem>secondParameter</ListItem>
</FormSectionNames>
</FormSection>
<FormItem name="firstParameter"> <parameter name="label">first_param_label</parameter>
</FormItem>
<FormItem name="secondParameter"> <parameter name="label">second_param_label</parameter>
</FormItem>
</Form>
</Folder>
title_key=This is the title/heading that appears at the top of the form
Advanced_Title=This is the title heading for the section for Advanced Users
first_param_label=First Param Label
second_param_label=Second Param Label
Forms definition elements
<Form name="com.ibm.di.connector.FileConnector"> <TranslationFile>NLS/idi_conn_filesys</TranslationFile>
<FormItemNames>
<ListItem>filePath</ListItem>
<ListItem>fileAwaitDataTimeout</ListItem>
<ListItem>fileAppend</ListItem>
<ListItem>exclusiveLock</ListItem>
<ListItem>$GLOBAL.debug</ListItem>
<ListItem>$GLOBAL.help</ListItem>
</FormItemNames>
<FormSectionNames>
<ListItem>$Mode-General</ListItem>
<ListItem>$Mode-Advanced</ListItem>
</FormSectionNames>
<FormSection name="Iterator-General"> <FormSectionNames>
<ListItem>filePath</ListItem>
</FormSectionNames>
<FormItem name="filePath"> <parameter name="description">path_desc_in</parameter>
</FormItem>
<parameter name="title">General_title</parameter>
</FormSection>
<FormSection name="AddOnly-General"> <FormSectionNames>
<ListItem>filePath</ListItem>
<ListItem>fileAppend</ListItem>
</FormSectionNames>
<FormItem name="filePath"> <parameter name="description">path_desc_out</parameter>
</FormItem>
<parameter name="title">General_title</parameter>
</FormSection>
<FormSection name="Iterator-Advanced"> <FormSectionNames>
<ListItem>fileAwaitDataTimeout</ListItem>
<ListItem>exclusiveLock</ListItem>
</FormSectionNames>
<FormItem name="exclusiveLock"> <parameter name="description">exlock_desc_in</parameter>
</FormItem>
</FormSection>
<FormSection name="AddOnly-Advanced"> <FormSectionNames>
<ListItem>exclusiveLock</ListItem>
</FormSectionNames>
<FormItem name="exclusiveLock"> <parameter name="description">exlock_desc_out</parameter>
</FormItem>
</FormSection>
<FormItem name="exclusiveLock"> <parameter name="label">exlock_label</parameter>
<parameter name="description">exlock_desc</parameter>
<parameter name="syntax">boolean</parameter>
</FormItem>
<FormItem name="fileAppend"> <parameter name="description">append_desc</parameter>
<parameter name="label">append_label</parameter>
<parameter name="syntax">boolean</parameter>
</FormItem>
<FormItem name="fileAwaitDataTimeout"> <Values>
<ListItem>-1</ListItem>
<ListItem>10</ListItem>
<ListItem>60</ListItem>
</Values>
<parameter name="description">time_desc</parameter>
<parameter name="label">time_label</parameter>
<parameter name="syntax">DROPEDIT</parameter>
</FormItem>
<FormItem name="filePath"> <Values>
<ListItem><></ListItem>
</Values>
<parameter name="description">path_desc</parameter>
<parameter name="label">path_label</parameter>
<parameter name="script">selectFile</parameter>
<parameter name="scriptLabel">path_sript_label</parameter>
<parameter name="scripthelp">path_script_help</parameter>
<parameter name="syntax">DROPEDIT</parameter>
</FormItem>
<parameter name="title">CONN_TITLE</parameter>
</Form>
<LocalizedValues>
<Item>
<Key>After every database operation</Key>
<Value>Localized.After.every.database.operation</Value>
</Item>
<Item>
<Key>After every database operation (Including Select)</Key>
<Value>Localized.After.every.database.operation.Including.Select</Value>
</Item>
<Item>
<Key>Manual</Key>
<Value>Localized.Manual</Value>
</Item>
<Item>
<Key>On Connector close</Key>
<Value>Localized.On.Connector.close</Value>
</Item>
</LocalizedValues>
XML Translation considerations
<TranslationFile>NLS/idi_conn_filesys</TranslationFile>
Parameter Definitions
Table 87. FormItem parameters
Keyword
Description
label
The label appearing in the left column of the
form (for example, LDAP URL)
description
The tooltip for the parameter
default
Default value for the parameter. The preferred
way of providing a default value is in the component configuration
itself (in the tdi.xml file). This default value will only be set
if the user uses the CE to view/modify the configuration for the component.
script2
Specifying this parameter adds a button to the
right of the input field. When the button is clicked, the named JavaScript
function is executed.
scriptLabel2
The button text
scriptHelp2
Tooltip for the script button
syntax
Specifies the syntax of the parameter. This
also affects the choice of UI control used to represent the value.
See the syntax section for more info.
reflect
If present the binding will use this method
to get/set the parameter value. The binding will prepend "get" or
"set" accordingly to this value (for example, specify Name to invoke
getName and setName). This is only used when the configuration object
performs specific logic when getting/setting a parameter value. For
component developers this is rarely needed as component configurations
only have get/set primitives.
Dynamic Values
Table 88.
Value
Description
@ASSEMBLYLINES@
Adds all known AssemblyLines to the array
@CONNECTORS@
Adds all known connectors to the array
@PARSERS@
Adds all known parsers to the array
@FUNCTIONS@
Adds all known function components to the array
@ATTRS@
Adds all attributes from the input map
Syntax
Table 89.
Value
Description
String
This is the default syntax. A one line text
field is created for text input.
Password
A password field is created for text input.
Be aware that if the user has configured a password store then FormUI
will not insert the value in the configuration object but insert a
property reference. The actual value is then stored in the password
store.
Boolean
A checkbox is created for true/false values
Dropedit
Dropdown with values from the values parameter.
Dropedit is the editable version where the user also has a text field
to specify a custom value. See Dynamic Values for special values.
TextArea
Creates a text area control for multi-line text
input
Script
Creates a button that invokes a script
Static
Creates a text label for viewing only (same
as TextArea, but readonly)
EditorWindow
This syntax causes the form to be a tabbed pane.
The non-editorwindow parameters appear in the left most tab whereas
each editorwindow parameter has its own tab with an editor input control.
Used when we need the complete display area for input (for example, scripts)
Component
This enables you to provide our own UI component
if we need complex input mechanisms or otherwise want more control
over the UI. Specify the java class name in the component keyword
that we want inserted into the form:
component:pub.test.CustomUI
package pub.test;
import org.eclipse.swt.widgets.Composite;
import com.ibm.tdi.eclipse.widgets.FormWidget2;
import com.ibm.di.config.interfaces.BaseConfiguration;
public class CustomUI extends Composite {
/*
* form - the FormWidget2 object
* parent - The Composite in which this control is placed
* config - the config object being edited
* paramname - the parameter of config being edited
*/
public CustomUI(FormWidget2 form, Composite parent, BaseConfiguration config,
String paramname) {
super(parent, 0);
}
}
Form Scripts
Examples
Connector Reconnect Rules definition
<Connector name="CustomConnector"> <Configuration>
... various configuration options ...
</Configuration>
<Reconnect>
<ReconnectRules>
<Rule>
<parameter name="exceptionClass">java.sql.SQLException</parameter>
<parameter name="exceptionMessageRegExp">^I/O.*</parameter>
<parameter name="action">reconnect</parameter>
</Rule>
<Rule>
<parameter name="exceptionClass">java.sql.SQLException</parameter>
<parameter name="exceptionMessageRegExp">^Io.*</parameter>
<parameter name="action">reconnect</parameter>
</Rule>
... more rules go here ...
</ReconnectRules>
</Reconnect>
exceptionClass: fully qualified name of the Java class of the exception
exceptionMessageRegExp: regular expression in Java syntax
action: error or reconnect
<Connector name="ibmdi.ReconnectTest"> <Configuration>
<parameter name="connectorType">com.ibm.di.connector.ReconnectTestConnector</parameter>
</Configuration>
<Reconnect>
<ReconnectRules>
<Rule>
<parameter name="exceptionClass">java.io.IOException</parameter>
<parameter name="exceptionMessageRegExp">.*file not found.*</parameter>
<parameter name="action">error</parameter>
</Rule>
<Rule>
<parameter name="action">reconnect</parameter>
</Rule>
</ReconnectRules>
</Reconnect>
</Connector>
Packaging and deploying the Connector