All Tivoli® Directory Integrator 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 Tivoli Directory Integrator. Usually the Connectors you write will not require all the options provided by Tivoli Directory Integrator and you 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 you 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 you will usually need to implement:
In the constructor you will usually set the name of your 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 you 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" you 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 you 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 you 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 you 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 your Connector. And remember, you only need to implement the methods corresponding to the Connector modes that your Connector will support.
When you write a connector you should take into account that users may call the connector methods in no particular order. This means you should have sanity checks on each method in case the connector requires certain methods to be called before others. From a Tivoli Directory Integrator server perspective the methods called on a connector is determined by the value of the mode parameter. In this section you 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.
Mode | Methods | Comments |
---|---|---|
Iterator |
|
getNextEntry should return NULL to signal end of input. |
AddOnly |
|
|
Lookup |
|
If you find more than one entry you should use clearFindEntries() and addFindEntry() for each entry found in this method. |
Delete |
|
|
Update |
|
If findEntry returns a single entry, modEntry will be called. If findEntry returns null, putEntry will be called. |
Delta |
|
See note below. |
First of all, to enable delta mode for your connector you must 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 you can code your connector by translating a delta Entry object to the underlying protocol of your 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 your connector's putEntry(), modEntry() or deleteEntry() methods, bypassing findEntry() and the algorithm to compute the delta entry.
In Tivoli Directory Integrator 7.1.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 your 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:
<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>
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:
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 |
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), you can invoke the initParser() method to initialize the associated parser:
/**
* 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;
You have to provide the input and/or output streams the parser will use for its read/write operations. The mode of your 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 you should do so unless there is a good reason to initialize it elsewhere. In any case you 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 you 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 you must 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 you can prefill the parserConfig section of your 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):
<Connector name="ibmdi.FileSystem">
<Configuration>
...
<parameter name="parserOption">Required</parameter>
</Configuration>
</Connector>
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:
<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>
Your connector has access to the ConnectorConfig object via the Connector.getConfiguration() method. Through the ConnectorConfig object you can obtain the ParserConfig interface object for the connector. Use that object to configure the parser before you invoke the initParser() method:
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);
Once the parser has been initialized you 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 you can invoke the hasParser() method to determine whether a parser is configured or not:
if(hasParser())
doSomething();
If you use multiple instances of the parser during the life time of your connector you 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 you use but the following method calls should be sufficient for most parsers:
// Close parser to release system resources
if(getParser() != null)
getParser().closeParser();
// assuming you just got a new input stream ... reinitialize the parser
initParser(inputStream, null);
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:
/**
* 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)
You can call these methods with code like
logmsg("initializing my connector");
This will cause your string to be issued to the AssemblyLine's configured log appenders, at INFO level. If you want to do more advanced logging, the com.ibm.di.connector.Connector class also has this field:
/**
* The log object for logging messages
*/
protected com.ibm.di.server.Log myLog;
This com.ibm.di.server.Log class has many methods for logging. You could therefore use the myLog object to do logging like this:
myLog.logerror("Something very bad happened");
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 your Connector, set up your CLASSPATH to include the jar files from the "jars/common" folder of the IBM® Tivoli Directory Integrator installation. At minimum you would need to include "miserver.jar" and "miconfig.jar".
When you create a custom Tivoli Directory Integrator component you also have to provide an additional file that describes your component to TDI. This file is located at the root of your 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 you have when you define a form. This form definition is used by the Tivoli Directory Integrator Configuration Editor to let the user configure your component. While the UI options in the form definition are basic and somewhat limited, you can still perform advanced operations using your 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 Tivoli Directory Integrator Configuration file.
A skeleton for the file could look something like this:
<?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>
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
key=value
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:
Component Type | Minimum Section Contents |
---|---|
Connector |
|
Parser |
|
Function |
|
In addition you should always include a form definition for each of your components. This is to prevent the configuration editor to report errors of missing forms. If your component has no configurable parameters you should include a form that says so.
When you start either the configuration editor or the server there is a component called the Tivoli Directory Integrator 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.
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 you 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 Tivoli Directory Integrator global parameter that enables detailed logging when checked.
<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>
The translation file (CustomConnector_en.properties) would contain:
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
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 you 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 you to show parameters depending on the mode of the Connector.
Here is a somewhat complex example of a complete form:
<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>
Definition of XML Tags:
<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>
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:
<TranslationFile>NLS/idi_conn_filesys</TranslationFile>
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.
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. |
script 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.
Script2 allows for a second button to the right of the first one. |
scriptLabel scriptLabel2 |
The button text |
scriptHelp 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. |
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.
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 |
The syntax parameter for a FormItem can have any of the following values:
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.
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. |
Boolean | A checkbox is created for true/false values |
Droplist 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 you need the complete display area for input (for example, scripts) |
Component | This enables you to provide your own UI component
if you need complex input mechanisms or otherwise want more control
over the UI. Specify the java class name in the component keyword
that you want inserted into the form:
syntax:component
component:pub.test.CustomUI
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 you 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 Tivoli Directory Integrator 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 Tivoli Directory Integrator Server.1
The rules for the particular Connector appear in the "connectors" section as a sibling of the "connectorConfig" sub-section like this:
<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>
Each rule has the following parameters:
exceptionClass: fully qualified name of the Java class of the exception
exceptionMessageRegExp: regular expression in Java syntax
action: error or reconnect
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.
<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>
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 you need to do is create a jar file (typically with the same name as that of the Connector) and include in it:
After you have created the jar file of the new Connector, you need only drop that jar file in the "jars/connectors" folder of the IBM Tivoli Directory Integrator installation. The next time the system starts up, it will automatically load the new Connector and make it ready for use.