GitHubContribute in GitHub: Open doc issue|Edit online

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:

  1. To set up a Library asset, complete the following steps:

  2. Create a connector under Resources > Connectors in the custom target project.

  3. Name the new connector CSVFile.

  4. Click the Output Map tab of the connector.

  5. Click Add, and specify * to write all available attributes to the target.

  6. Set the mode to AddOnly.

  7. 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.

    1. To assign a value to the File Path parameter, click the label or the pencil icon next to the field.

    2. 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.

  8. 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 initializeoperation is started.

  1. Click More . . . in the Connection Editor to view more options.
  2. 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.

  1. To Access the AssemblyLine hooks, click Options . . . in the AssemblyLine Editor.

  2. Click AssemblyLine Hooks.

  3. 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.

  1. 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 
    
  2. 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";
    
  3. 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);.

  4. Use similar snippets for the other conditional branches, such as ELSE-IF: Entry is Group:

    return "group".equals(typeOfEntry);
    
  5. 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);
    
  6. After the structure is created, place the components and script logic under each branch to deal with the actual operations and entry types.

  7. 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.

  8. 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 support Lookup mode. To prevent data from being written for this operation and to ensure that only connector initialization is done, the Override Add hook is enabled, but no script is written in it. It causes the actual add operation of AddOnly 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 the Work 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 to Lookup mode. Place the following hook scripts in the On Multiple Entries and On 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 the Work 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 the modEntry 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 the WriteToTarget AssemblyLine for this entry.

    putEntry
    Connector mode: Update.

    If Update mode is used, then the Link Criteria requirements are the same as for findEntry operation.

    In this example, the AddOnly mode is used because the File Connector does not support searching and Update 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. The Link Criteria requirements are the same as for the findEntry operation.

    deleteEntry
    Connector mode: Delete.

    The Link Criteria requirements are the same as for findEntry. As with modEntry, 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:

  1. Drag the CSV File Connector from the library and place it on the branch. An instance of the connector appears under this branch.
  2. Set the connector to AddOnly mode. Enable the Override 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:

  1. Click CSVFile Connector under IF: Initialize operation.
  2. Click Re-use connector. A new connector is displayed.
  3. Name the new connector as CSVFile Add.
  4. Drag CSVFile Add and place it on the ELSE-IF: Add or Modify branch. It now appears under this branch.
  5. 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.
    1. Expand CSVFile Add.
    2. Click Override Add hook.
    3. Right-click and click Change Inheritance.
    4. 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.