Example: WriteToTarget AssemblyLine for CSV file target
The example WriteToTarget AssemblyLine shows the configuration and scripts that you can use to set up a CSV file as a custom target for Federated Directory Server.
Note: You can right-click and download the example configuration file for the CSV file custom target: FDS_Target.xml
.
Configuration
You must complete the following configuration steps before you set up the WriteToTarget AssemblyLine:
Set up the Library Connector
You must configure a connector in the Resources Library of the custom target project. This connector is inherited into the AssemblyLine.
The following steps show how to use a File Connector to handle write operations to a target CSV file in an example scenario:
-
To set up a Library asset, complete the following steps:
-
Create a connector under Resources > Connectors in the custom target project.
-
Name the new connector CSVFile.
-
Click the Output Map tab of the connector.
-
Click Add, and specify * to write all available attributes to the target.
-
Set the mode to AddOnly.
-
On the Connection tab, set the File Path to dynamically use the value that is passed in the
opEntry
attribute target.filepath. Only this parameter is used on the Connection tab.-
To assign a value to the File Path parameter, click the label or the pencil icon next to the field.
-
Select the Expression Editor window, select Advanced (JavaScript), and enter the following code in the field:
return connectorParam("target.filepath");
In this example, no default value is provided in the call to the
connectorParam()
as the optional second argument. If the specified property is not set, the standard error for missing connector parameters is displayed. -
-
Select the CSV Parser for the connector on the Parser tab.
Define hooks to return status
To ensure that errors and successful operations are correctly signaled back to Federated Directory Server, the following hooks must be scripted for the Library Connector. These hooks are listed under the
DataFlow (AddOnly) section in the
Hooks tab of the connector.
Default Success
Example script:
// return successful write operation
tcb["$writeStatus"] = "success";
tcb["$status"] = "success";
// indicate that it was an Add operation
tcb["$operation"] = "add";
// flag that a change has been made
tcb["$change"] = true;
Default On Error
Example script:
// make sure the tcb variable is available
tcb = task.getTCB();
// only the $status need be set in this case
tcb["$status"] = "failed";
// and of course, the error passed to FDS
tcb["$error"] = error;
// call LDAPSync error handling routine
defaultErrorHandling("ERROR");
Hook logic in the Library Connector enables reuse of this component in the WriteToTarget AssemblyLine. It also facilitates maintenance because error handling code is kept in one place.
Specify when the connector must initialize
By default, all components initialize when the AssemblyLine starts. However, you must configure the connector to initialize when the initialize
operation is started.
- Click More . . . in the Connection Editor to view more options.
- From the Initialize list, select only when used.
This setting specifies that the connector must initialize when it is first encountered during standard AssemblyLine processing, which is under the IF: Initialize operation
branch.
Initialize the LDAPSync utility for WriteToTarget
The WriteToTarget AssemblyLine uses functions and variables that are provided by the referenced LDAPSync project. The LDAPSync utility library must be initialized for the functions and variables
to work correctly.
Add the initialization code to the script of the AssemblyLine's Prolog - Before Initialization hook.
-
To Access the AssemblyLine hooks, click Options . . . in the AssemblyLine Editor.
-
Click AssemblyLine Hooks.
-
In Prolog - Before Initialization Hook, enter the following code:
initUtils();
This hook is run before processing begins on the AssemblyLine and ensures that the LDAPSync
utility library is initialized and ready to use.
WriteToTarget AssemblyLine example
Unlike most common AssemblyLines, the WriteToTarget AssemblyLine does not have an Iterator
mode connector in the Feed section. Instead, the Work Entry
is passed whenever Federated Directory Server starts
the AssemblyLine. All work with this data is done by components in the Data Flow
section.
In the example, the Data Flow
section starts with an initialization script. This script is followed by a set of IF-ELSE branches that are designed to handle the various operations and entry types that are passed from Federated Directory
Server.
-
Use the following structure, which is a snapshot of the example WriteToTarget AssemblyLine:
Feed Data Flow Initialize IF: Initialize operation IF: Entry is Person IF: FindEntry Operation ELSE-IF: Add or Modify ELSE-IF: Delete ELSE-IF: Entry is Group IF: FindEntry operation for Group ELSE-IF: Add or Modify for Group ELSE-IF: Delete for Group
-
Store the TCB attribute values in variables in the
Initialize
script at the start of the AssemblyLine. It makes the code more legible in the AssemblyLine. Also, set the initial values for the return TCB attributes.// first the TCB is referenced by a variable tcb = task.getTCB(); // TCB attributes are store in variables typeOfEntry = tcb.getString("$typeOfEntry"); operation = tcb.getString("$operation"); search = tcb.getObject("$search") // then return TCB attributes are set to default values tcb["$change"] = false; tcb["$writeStatus"] = "failed"; tcb["$status"] = "failed";
-
In each IF and IF-ELSE branch, or conditional branches, use the operation or typeOfEntry variable to filter out the function or entry type that components under this branch must handle. Use script code for the conditional branches to return true or false, and to control how the branch behaves.
For example, script the
IF: Initialize operation
branch with this code:return "initialize".equals(operation);
. -
Use similar snippets for the other conditional branches, such as
ELSE-IF: Entry is Group
:return "group".equals(typeOfEntry);
-
In some cases, you can test multiple values, such as in the
ELSE-IF: Add or Modify
branch:return "putEntry".equals(operation) || "modEntry".equals(operation);
-
After the structure is created, place the components and script logic under each branch to deal with the actual operations and entry types.
-
Use the Library Connector in the AssemblyLine so that its hook logic is available, which facilitates implementation. To use the Library Connector, drag it from the Library to the AssemblyLine and place it on the branch under which it must appear. Alternatively, click Add Component, and select Library Connector from the list.
-
Depending on the branch under which a copy of the connector is placed, set the connector mode for the operations: initialize
Connector mode:Lookup
.In this example, the
AddOnly
mode is used because the File Connector does not supportLookup
mode. To prevent data from being written for this operation and to ensure that only connector initialization is done, theOverride Add
hook is enabled, but no script is written in it. It causes the actual add operation ofAddOnly
mode to do nothing.findEntry
Connector mode:Lookup
.The
Link Criteria
is set up so that an attribute in the target is matched to an attribute in theWork Entry
that is passed in by Federated Directory Server. This match must be exclusive for each entry in the target, for example, the$dn
of an LDAP directory, or UNID of a Notes Document.In this example, special handling is required for the
findEntry
operation by using the Library Connector set toLookup
mode. Place the following hook scripts in theOn Multiple Entries
andOn No Match
hooks, which are in the DataFlow (Lookup) section of the Hooks:On Multiple Entries
This hook is called when there are multiple matches.// set up connEntry collection Attribute connEntry = system.newEntry("$conn"); // add entries found to connEntry do { foundEntry = thisConnector.getNextDuplicateEntry(); if (foundEntry !== null) { connEntry.addValue(foundEntry); } } while (foundEntry !== null); // return as $conn in the Work Entry work["$conn"] = connEntry // flow will continue ot Default Success Hook
On No Match
This hook is called when no match is found.// empty the Work Entry to signal nothing found work.removeAllAttributes(); task.setWork(null); // flow will continue to Default Success Hook
In this example, the
findEntry
logic is scripted to return that no match is found.The input map of the
Lookup
mode connector provides the attributes that are returned in theWork Entry
to Federated Directory Server. This information is compared with the attributes that are mapped in Federated Directory Server. If there are any differences, then themodEntry
operation is started. If no differences in attributes or their values are detected, Federated Directory Server determines that the write operation is unnecessary and does not call theWriteToTarget
AssemblyLine for this entry.putEntry
Connector mode:Update
.If
Update
mode is used, then theLink Criteria
requirements are the same as forfindEntry
operation.In this example, the
AddOnly
mode is used because the File Connector does not support searching andUpdate
mode involves doing a search.modEntry
Connector mode:Update
.In this example, the File Connector is unable to randomly update entries. However, the
findEntry
operation is scripted to never return a match; so this operation is never called. TheLink Criteria
requirements are the same as for thefindEntry
operation.deleteEntry
Connector mode:Delete
.The
Link Criteria
requirements are the same as forfindEntry
. As withmodEntry
, this operation must never be called for this example.
The passing of status is handled by the hooks of the Library Connector as described in the previous section, with one exception. The findEntry
operation is an exception because it must not only return status, but it must also return
the entry or entries that are found by the lookup operation.
Custom target CSV file example
In this example, the WriteToTarget AssemblyLine adds new entries only because the target is a file. Also, the branching logic is simplified to handle only the operations because all entry types are written to the same file.
The following simplified structure is used for this example:
Feed
Data Flow
Initialize
IF: Initialize operation
IF: FindEntry operation
ELSE-IF: Add or Modify
ELSE-IF: Delete
Initialize
The TCB attribute values are stored in variables in the Initialize
script at the start of the AssemblyLine. The initial values for the return TCB attributes are also set in this script.
// first the TCB is referenced by a variable
tcb = task.getTCB();
// TCB attributes are store in variables
typeOfEntry = tcb.getString("$typeOfEntry");
operation = tcb.getString("$operation");
search = tcb.getObject("$search")
// then return TCB attributes are set to default values
tcb["$change"] = false;
tcb["$writeStatus"] = "failed";
tcb["$status"] = "failed";
IF: Initialize operation
The following script is used for the IF branch:
return "initialize".equals(operation);
To handle the initialize operation:
- Drag the CSV File Connector from the library and place it on the branch. An instance of the connector appears under this branch.
- Set the connector to
AddOnly
mode. Enable theOverride Add
hook and leave it empty to prevent any write operation.
ELSE-IF: FindEntry operation
The following script is used for the ELSE-IF branch:
return "findEntry".equals(operation);
The delete and modify operations are not required because this example writes to a file and the File Connector does not support searches. The lookup operation (findEntry
) must return a status of not found to Federated Directory
Server so that these write operations are never called.
Only the following script is required. Add an Empty Script Component under this branch and paste the following script in it:
work.removeAllAttributes();
task.setWork(null);
This script empties the Work Entry
to indicate that the findEntry
operation failed to find a match.
ELSE-IF: Add or Modify
Use the following script in this branch to determine its condition:
return "putEntry".equals(operation)
|| "modify".equals(operation);
Another copy of the Library Connector is required under this branch. It must reuse the same connection that the connector under the IF: Initialize operation branch uses to ensure that both Connectors work with the same file.
To reuse the AssemblyLine Connector:
- Click CSVFile Connector under
IF: Initialize operation
. - Click Re-use connector. A new connector is displayed.
- Name the new connector as CSVFile Add.
- Drag CSVFile Add and place it on the ELSE-IF: Add or Modify branch. It now appears under this branch.
- When you reuse a connector, by default, all aspects of the original connector are reused, including the hooks. The Override Add hook, which is enabled for the
CSVFile
connector, must not be enabled for this copy, as it prevents write operations.- Expand CSVFile Add.
- Click Override Add hook.
- Right-click and click Change Inheritance.
- Select [no inheritance] from the Select Inheritance options. The Override Add hook is removed from the list of enabled hooks for the CSVFile Add connector.
Alternatively, you can make the WriteToTarget AssemblyLine read the existing file to memory, such as a java.util.HashMap
, during the initialization operation call. The subsequent write operations must apply all changes to the stored
values, and then commit the resulting entries to the file during terminate handling. You can use this approach to provide full Federated Directory Server functionality for any sequential data store that is a custom target.
ELSE-IF: Delete
Use the following script for this branch:
return "delete".equals(operation);
Under the branch, add an Empty Script Component with the following code:
logmsg("ERROR", "This solution does not support delete");
Control must not be passed to this branch because the findEntry operation never finds a match.