5 Developing Identity Connectors Using Java

You can develop an identity connector using the Identity Connector Framework (ICF) and Oracle Identity Manager metadata.

This chapter is a tutorial that walks through the procedures necessary to develop an identity connector using the Identity Connector Framework (ICF) and the Oracle Identity Manager metadata. It includes information about important ICF classes and interfaces, the connector bundle, the connector server, and code samples for implementing a flat file identity connector and creating Oracle Identity Manager metadata for user provisioning and reconciliation processes.

This chapter contains the following sections:

5.1 Introduction to Flat File Connector Development

To develop a flat file connector, you must develop an implementation of the Configuration interface followed by the implementation of the Connector class.

Before beginning, you must prepare IO representation modules for all flat file connector operations. This might include all or some of the following:

  • Read the column names of the flat file and prepare metadata information.

  • Add a record to the flat file with the corresponding column values separated by the specified delimiter.

  • Delete a record to the flat file based on the UID value.

  • Search operations on flat file.

This tutorial is focused on identity connector development, and therefore, these preparations are not discussed in detail.

Note:

The following supporting classes are used for file input and output handling during identity connector operations:

  • org.identityconnectors.flatfile.io.FlatFileIOFactory

  • org.identityconnectors.flatfile.io.FlatFileMetadata

  • org.identityconnectors.flatfile.io.FlatFileParser

  • org.identityconnectors.flatfile.io.FlatFileWriter

See Supporting Classes for File Input and Output Handling for the implementations of the input and output handling supporting classes.

5.2 Developing a Flat File Connector

Developing a flat file connector involves implementing the AbstractConfiguration, PoolableConnector, and AbstractFilterTranslator classes, and creating the connector bundle JAR file.

This section describes the high-level procedure to develop a flat file connector along with code samples. It contains the following topics:

5.2.1 Overview of Developing a Flat File Connector

Developing a flat file connector involves implementing the AbstractConfiguration, PoolableConnector, and AbstractFilterTranslator classes, and creating the connector bundle JAR file.

To develop a flat file connector:

  1. Implement the configuration class for the Flat File Connector by extending the org.identityconnectors.framework.spi.AbstractConfiguration base class.

    See Implementation of AbstractConfiguration for a sample implementation of the configuration class.

    See The org.identityconnectors.framework.spi.Configuration Interface for more information.

  2. Create connector class for the Flat File Connector by implementing the org.identityconnectors.framework.spi.Connector interface.

    See Implementation of PoolableConnector for a sample implementation of the PoolableConnector class.

  3. This connector supports only the ContainsAllValuesFilter operation. Implement the ContainsAllValuesFilter operation. See Implementation of AbstractFilterTranslator for a sample implementation of the of the AbstractFilterTranslator<T> class.

  4. Create the connector bundle JAR. The MANIFEST.MF file must contain the following entries:

    • ConnectorBundle-FrameworkVersion

    • ConnectorBundle-Name

    • ConnectorBundle-Version

    See The MANIFEST.MF File for the contents of the MANIFEST.MF file.

  5. Update the connector bundle JAR as created in step 4. To do so:

    1. Extract the connector bundle JAR into any desired location.

    2. Create a lib directory in the directory in which you extracted the JAR.

    3. Add the dependent third-party JARs into the lib directory.

    4. JAR the entire directory.

      Note:

      The MANIFEST.MF file must contain the entries listed in step 4.

5.2.2 Implementation of AbstractConfiguration

The AbstractConfiguration base class can be extended to implement the configuration class for a Flat File Connector.

The following is a sample of the implementation of the AbstratConfiguration class:

package org.identityconnectors.flatfile;
import java.io.File;
import org.identityconnectors.flatfile.io.FlatFileIOFactory;
import org.identityconnectors.framework.common.exceptions.ConfigurationException;
import org.identityconnectors.framework.spi.AbstractConfiguration;
import org.identityconnectors.framework.spi.ConfigurationProperty;
/**
 * Class for storing the flat file configuration 
 */
public class FlatFileConfiguration extends AbstractConfiguration {
/*
 * Storage file name
 */
private File storeFile;
/*
 * Delimeter used
 */
private String textFieldDelimeter;	
/*
 * Unique attribute field name
 */
private String uniqueAttributeName = "";	
/*
 * Change attribute field name. Should be numeric
 */
private String changeLogAttributeName = "";
 
public File getStoreFile() {
return storeFile;
}
 
public String getTextFieldDelimeter() {
return textFieldDelimeter;
}
 
     public String getUniqueAttributeName() {
        return uniqueAttributeName;
    }
 
    public String getChangeLogAttributeName() {
        return changeLogAttributeName;
    }
 
    /**
     * Set the store file
     * @param storeFile
     */
    @ConfigurationProperty(order = 1, helpMessageKey = "USER_ACCOUNT_STORE_HELP", 
            displayMessageKey = "USER_ACCOUNT_STORE_DISPLAY")
    public void setStoreFile(File storeFile) {
        this.storeFile = storeFile;
    }
 
    /**
     * Set the text field delimeter
     * @param textFieldDelimeter
     */
    @ConfigurationProperty(order = 2, 
            helpMessageKey = "USER_STORE_TEXT_DELIM_HELP", 
            displayMessageKey = "USER_STORE_TEXT_DELIM_DISPLAY")
    public void setTextFieldDelimeter(String textFieldDelimeter) {
        this.textFieldDelimeter = textFieldDelimeter;
    }
 
    /**
     * Set the field whose values will be considered as unique attributes
     * @param uniqueAttributeName
     */
    @ConfigurationProperty(order = 3, helpMessageKey = "UNIQUE_ATTR_HELP", 
            displayMessageKey = "UNIQUE_ATTR_DISPLAY")
    public void setUniqueAttributeName(String uniqueAttributeName) {
        this.uniqueAttributeName = uniqueAttributeName;
    }
 
    /**
     * Set the field name where change number should be stored
     * @param changeLogAttributeName
     */
    @ConfigurationProperty(order = 3, helpMessageKey = "CHANGELOG_ATTR_HELP", 
            displayMessageKey = "CHANGELOG_ATTR_DISPLAY")
    public void setChangeLogAttributeName(String changeLogAttributeName) {
        this.changeLogAttributeName = changeLogAttributeName;
    }    
    @Override
    public void validate() {
        
        // Validate if file exists and is usable
        boolean validFile = (this.storeFile.exists() &&
                this.storeFile.canRead() &&
                this.storeFile.canWrite() &&
                this.storeFile.isFile());
        
        if (!validFile)
            throw new ConfigurationException("User store file not valid");
        
        // Validate if there is a field on name of unique attribute field name        
        // Validate if there is a field on name of change attribute field name
        FlatFileIOFactory.getInstance(this);
        // Initialization does the validation
    }
    
    
}

5.2.3 Implementation of PoolableConnector

The org.identityconnectors.framework.spi.Connector interface is implemented to create the connector class for a Flat File Connector.

The following code sample implements the CreateOp, DeleteOp, SearchOp and UpdateOp interfaces and thus supports all four operations. The FlatFileMetadata, FlatFileParser and FlatFileWriter classes are supporting classes. Their implementation is not shown as they do not belong to the ICF.

package org.identityconnectors.flatfile;
 
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import org.identityconnectors.flatfile.io.FlatFileIOFactory;
import org.identityconnectors.flatfile.io.FlatFileMetadata;
import org.identityconnectors.flatfile.io.FlatFileParser;
import org.identityconnectors.flatfile.io.FlatFileWriter;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.SchemaBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.PoolableConnector;
import org.identityconnectors.framework.spi.operations.CreateOp;
import org.identityconnectors.framework.spi.operations.DeleteOp;
import org.identityconnectors.framework.spi.operations.SchemaOp;
import org.identityconnectors.framework.spi.operations.SearchOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;
 
/**
 * The main connector class
 */
@ConnectorClass(configurationClass = FlatFileConfiguration.class, displayNameKey = "FlatFile")
public class FlatFileConnector implements SchemaOp, CreateOp, DeleteOp,
        UpdateOp, SearchOp<Map<String, String>>, GetApiOp, PoolableConnector {
 
    private FlatFileConfiguration flatFileConfig;
    private FlatFileMetadata flatFileMetadata;
    private FlatFileParser flatFileParser;
    private FlatFileWriter flatFileWriter;
    private boolean alive = false;
 
    @Override
    public Configuration getConfiguration() {
        return this.flatFileConfig;
    }
 
    @Override
    public void init(Configuration config) {
        this.flatFileConfig = (FlatFileConfiguration) config;
 
        FlatFileIOFactory flatFileIOFactory = 
             FlatFileIOFactory.getInstance(flatFileConfig);
        this.flatFileMetadata = flatFileIOFactory.getMetadataInstance();
        this.flatFileParser = flatFileIOFactory.getFileParserInstance();
        this.flatFileWriter = flatFileIOFactory.getFileWriterInstance();
        this.alive = true;
        System.out.println("init called: Initialization done");
    }
 
    @Override
    public void dispose() {
        this.alive = false;
    }
 
    @Override
    public Schema schema() {
        SchemaBuilder flatFileSchemaBldr = new SchemaBuilder(this.getClass());
        Set<AttributeInfo> attrInfos = new HashSet<AttributeInfo>();
        for (String fieldName : flatFileMetadata.getOrderedTextFieldNames()) {
            AttributeInfoBuilder attrBuilder = new AttributeInfoBuilder();
            attrBuilder.setName(fieldName);
            attrBuilder.setCreateable(true);
            attrBuilder.setUpdateable(true);
            attrInfos.add(attrBuilder.build());
        }
        
        // Supported class and attributes
        flatFileSchemaBldr.defineObjectClass
          (ObjectClass.ACCOUNT.getDisplayNameKey(),attrInfos);
        System.out.println("schema called: Built the schema properly");
        return flatFileSchemaBldr.build();
    }
 
    @Override
    public Uid create(ObjectClass arg0, Set<Attribute> attrs,
            OperationOptions ops) {
 
        System.out.println("Creating user account " + attrs);
        assertUserObjectClass(arg0);
        try {
            FlatFileUserAccount accountRecord = new FlatFileUserAccount(attrs);
            // Assert uid is there
            assertUidPresence(accountRecord);
 
            // Create the user
            this.flatFileWriter.addAccount(accountRecord);
 
            // Return uid
            String uniqueAttrField = this.flatFileConfig
                    .getUniqueAttributeName();
            String uniqueAttrVal = accountRecord
                    .getAttributeValue(uniqueAttrField);
            System.out.println("User " + uniqueAttrVal + " created");
            
            return new Uid(uniqueAttrVal);
        } catch (Exception ex) {
 
            // If account exists
            if (ex.getMessage().contains("exists"))
                throw new AlreadyExistsException(ex);
 
            // For all other causes
            System.out.println("Error in create " + ex.getMessage());
            throw ConnectorException.wrap(ex);
        }
    }
 
    @Override
    public void delete(ObjectClass arg0, Uid arg1, OperationOptions arg2) {
        final String uidVal = arg1.getUidValue();
        this.flatFileWriter.deleteAccount(uidVal);
        System.out.println("Account " + uidVal + " deleted");
    }
 
    @Override
    public Uid update(ObjectClass arg0, Uid arg1, Set<Attribute> arg2,
            OperationOptions arg3) {
        String accountIdentifier = arg1.getUidValue();
        // Fetch the account
        FlatFileUserAccount accountToBeUpdated = this.flatFileParser
                .getAccount(accountIdentifier);
 
        // Update
        accountToBeUpdated.updateAttributes(arg2);
        this.flatFileWriter
                .modifyAccount(accountIdentifier, accountToBeUpdated);
        System.out.println("Account " + accountIdentifier + " updated");
 
        // Return new uid
        String newAccountIdentifier = accountToBeUpdated
                .getAttributeValue(this.flatFileConfig.getUniqueAttributeName());
        return new Uid(newAccountIdentifier);
    }
 
    @Override
    public FilterTranslator<Map<String, String>> createFilterTranslator(
            ObjectClass arg0, OperationOptions arg1) {
        // TODO: Create a fine grained filter translator
 
        // Return a dummy object as its not applicable here.
        // All processing happens in the execute query
        return new AbstractFilterTranslator<Map<String, String>>() {
        };
    }
 
    @Override
    public ConnectorObject getObject(ObjectClass arg0, Uid uid,
            OperationOptions arg2) {
        // Return matching record
        String accountIdentifier = uid.getUidValue();
        FlatFileUserAccount userAcc = this.flatFileParser
                .getAccount(accountIdentifier);
        ConnectorObject userAccConnObject = convertToConnectorObject(userAcc);
        return userAccConnObject;
    }
 
    /*
     * (non-Javadoc)
     * This is the search implementation. 
     * The Map passed as the query here, will map to all the records with 
     * matching attributes.
     * 
     * The record will be filtered if any of the matching attributes are not
     * found
     * 
     * @see
     * org.identityconnectors.framework.spi.operations.SearchOp#executeQuery
     * (org.identityconnectors.framework.common.objects.ObjectClass,
     * java.lang.Object,
     * org.identityconnectors.framework.common.objects.ResultsHandler,
     * org.identityconnectors.framework.common.objects.OperationOptions)
     */
    @Override
    public void executeQuery(ObjectClass objectClass,
            Map<String, String> matchSet, ResultsHandler resultHandler,
            OperationOptions ops) {
 
    System.out.println("Inside executeQuery");
    
        // Iterate over the records and handle individually
        Iterator<FlatFileUserAccount> userAccountIterator = this.flatFileParser
                .getAccountIterator(matchSet);
 
        while (userAccountIterator.hasNext()) {
            FlatFileUserAccount userAcc = userAccountIterator.next();
            ConnectorObject userAccObject = convertToConnectorObject(userAcc);
            if (!resultHandler.handle(userAccObject)) {
                System.out.println("Not able to handle " + userAcc);
                break;
            }
        }
    }
 
    private void assertUserObjectClass(ObjectClass arg0) {
        if (!arg0.equals(ObjectClass.ACCOUNT))
            throw new UnsupportedOperationException(
                    "Only user account operations supported.");
 
    }
 
    private void assertUidPresence(FlatFileUserAccount accountRecord) {
        String uniqueAttrField = this.flatFileConfig.getUniqueAttributeName();
        String uniqueAttrVal = accountRecord.getAttributeValue(uniqueAttrField);
 
        if (uniqueAttrVal == null) {
            throw new IllegalArgumentException("Unique attribute not passed");
        }
    }
 
    private ConnectorObject convertToConnectorObject(FlatFileUserAccount userAcc) {
        ConnectorObjectBuilder userObjBuilder = new ConnectorObjectBuilder();
        // Add attributes
        List<String> attributeNames = this.flatFileMetadata
                .getOrderedTextFieldNames();
        for (String attributeName : attributeNames) {
            String attributeVal = userAcc.getAttributeValue(attributeName);
            userObjBuilder.addAttribute(attributeName, attributeVal);
 
            if (attributeName.equals(this.flatFileConfig
                    .getUniqueAttributeName())) {
                userObjBuilder.setUid(attributeVal);
                userObjBuilder.setName(attributeVal);
            }
        }
        return userObjBuilder.build();
    }
 
    @Override
    public void checkAlive() {
        if (!alive)
            throw new RuntimeException("Connection not alive");
    }
 
}

5.2.4 Implementation of AbstractFilterTranslator

The org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T> class is implemented to define the filter operation.

The following is a sample implementation of org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T> that defines the filter operation:

package org.identityconnectors.flatfile.filteroperations;
 
import java.util.HashMap;
import java.util.Map;
 
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator;
import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter;
 
public class ContainsAllValuesImpl extends AbstractFilterTranslator<Map<String, String>>{
@Override
protected Map<String, String> createContainsAllValuesExpression(
ContainsAllValuesFilter filter, boolean not) {
Map<String, String> containsAllMap = new HashMap<String, String>();
Attribute attr = filter.getAttribute();
containsAllMap.put(attr.getName(), attr.getValue().get(0).toString());
return containsAllMap;
}
}

5.2.5 The MANIFEST.MF File

The MANIFEST.MF file is used to create the connector bundle JAR file.

The following is the contents of the MANIFEST.MF file:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 14.1-b02 (Sun Microsystems Inc.)
ConnectorBundle-FrameworkVersion: 1.0
ConnectorBundle-Name: org.identityconnectors.flatfile
ConnectorBundle-Version: 1.0
Build-Number: 609
Subversion-Revision: 4582

5.3 Supporting Classes for File Input and Output Handling

The supporting classes for file input and output handling are FlatFileIOFactory, FlatFileMetaData, FlatFileParser, FlatFileWriter, FlatfileLineIterator, FlatfileUserAccount, FlatfileAccountConversionHandler, and Messages.Properties.

This section shows the implementation of the following supporting classes for file input and output handling:

5.3.1 Implementation of the FlatFileIOFactory Supporting Class

The following code sample shows the implementation of the FlatFileIOFactory supporting class:

package org.identityconnectors.flatfile.io;
 
import org.identityconnectors.flatfile.FlatFileConfiguration;
 
public class FlatFileIOFactory {
    
    private FlatFileMetadata flatFileMetadata;
    private FlatFileConfiguration flatFileConfig;
    
    /**
     * Provides instance of the factory
     * @param flatfileConfig Configuration bean for the flat file
     */
    public static FlatFileIOFactory getInstance(FlatFileConfiguration fileConfig) {
        return new FlatFileIOFactory(fileConfig);        
    }
    
    /**
     * Making it private to avoid public instantiation. Encouraging use of getInstance
     * @param fileConfig
     */
    private FlatFileIOFactory(FlatFileConfiguration fileConfig) {
        this.flatFileConfig = fileConfig;
        this.flatFileMetadata = new FlatFileMetadata(flatFileConfig);
        System.out.println("Metadata set");
    }
    
    /**
     * Returns the metadata instance
     * @return
     */
    public FlatFileMetadata getMetadataInstance() {
        return this.flatFileMetadata;
    }
    
    /**
     * Returns the FlatFileParser instance
     * @return
     */
    public FlatFileParser getFileParserInstance() {
        return new FlatFileParser(this.flatFileMetadata, this.flatFileConfig);
    }
    
    /**
     * Returns the FlatFileWriter instance
     * @return
     */
    public FlatFileWriter getFileWriterInstance() {
        return new FlatFileWriter(this.flatFileMetadata, this.flatFileConfig);
    }
}

5.3.2 Implementation of the FlatFileMetaData Supporting Class

The following code sample shows the implementation of the FlatFileMetaData supporting class:

package org.identityconnectors.flatfile.io;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
 
import org.identityconnectors.flatfile.FlatFileConfiguration;
 
/**
 * This class contains all the metadata related information Example: Ordering of
 * columns, Number of columns etc.
 * 
 * @author harsh
 * 
 */
public class FlatFileMetadata {
 
    private FlatFileConfiguration fileConfig;
 
    private List<String> orderedTextFieldNames;
 
    private String changeLogFieldName;
    private String uniqueAttributeFiledName;
 
    /**
     * Instantiates the class with the file configuration.
     * Making it package private to encourage instantiation from Factory class
     * @param fileConfig
     */
    FlatFileMetadata(FlatFileConfiguration fileConfig) {
        /*
         * Ideally you should not take connector specific configuration class in
         * flat file resource classes. Change if this has to go to production.
         * Probably make another configuration class for flat file with same
         * signatures.
         */
        this.fileConfig = fileConfig;
 
        initializeMetadata();
        validateConfigProps();
    }
 
    /**
     * Returns the text field names in the order of their storage
     * 
     * @return
     */
    public List<String> getOrderedTextFieldNames() {
        return this.orderedTextFieldNames;
    }
 
    /**
     * Returns the number of columns
     */
    public int getNumberOfFields() {
        int numberOfTextFields = this.orderedTextFieldNames.size();
        return numberOfTextFields;
    }
 
    /**
     * Specifies if number of tokens are matching with the standard length of metadata
     * @param countTokens
     * @return
     */
    public boolean isDifferentFromNumberOfFields(int countTokens) {
        return (getNumberOfFields() != countTokens);
    }
    
    /**
     * Reads the header line and sets the metadata
     */
    private void initializeMetadata() {
        // Read the file.
        File recordsStore = this.fileConfig.getStoreFile();
 
        try {
            BufferedReader storeFileReader = new BufferedReader(new FileReader(
                    recordsStore.getAbsolutePath()));
 
            // Read the header line
            String headerString = storeFileReader.readLine();
 
            // Tokenize the headerString
            StringTokenizer tokenizer = new StringTokenizer(headerString,
                    fileConfig.getTextFieldDelimeter());
 
            this.orderedTextFieldNames = new ArrayList<String>();
            while (tokenizer.hasMoreTokens()) {
                String header = tokenizer.nextToken();
                this.orderedTextFieldNames.add(header);
            }
            
            System.out.println("Columns read - " + this.orderedTextFieldNames);
        } catch (IOException e) {
            throw new RuntimeException("How can I read a corrupted file");
        }
 
        // Store the change log and unique attribute field names
        this.changeLogFieldName = fileConfig.getChangeLogAttributeName();
        this.uniqueAttributeFiledName = fileConfig.getUniqueAttributeName();
    }
 
    /**
     * Validate if the attribute names in config props object are present in the
     * column names
     * 
     * @throws RuntimeException
     *             if validation fails
     */
    private void validateConfigProps() {
        // Check if unique attribute col name is present
        if (!this.orderedTextFieldNames.contains(this.changeLogFieldName))
            throw new RuntimeException("Change log field name "
                    + this.changeLogFieldName + " not found in the store file ");
 
        // Check if change col name is present
        if (!this.orderedTextFieldNames.contains(this.uniqueAttributeFiledName))
            throw new RuntimeException("Unique attribute field name "
                    + this.uniqueAttributeFiledName
                    + " not found in the store file");
    }
}

5.3.3 Implementation of the FlatFileParser Supporting Class

The following code sample shows the implementation of the FlatFileParser supporting class:

package org.identityconnectors.flatfile.io;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import org.identityconnectors.flatfile.FlatFileConfiguration;
import org.identityconnectors.flatfile.FlatFileUserAccount;
import org.identityconnectors.flatfile.utils.AccountConversionHandler;
 
public class FlatFileParser {
 
    private File recordsStore;
    private FlatFileConfiguration fileConfig;
    private FlatFileMetadata metadata;
    private AccountConversionHandler accountConverter;
 
    /**
     * Instantiates the parser class. Making it package private to encourage
     * instantiation from Factory class
     * 
     * @param metadata
     * @param fileConfig
     */
    FlatFileParser(FlatFileMetadata metadata, FlatFileConfiguration fileConfig) {
        this.fileConfig = fileConfig;
        this.recordsStore = fileConfig.getStoreFile();
        this.accountConverter = new AccountConversionHandler(metadata,
                fileConfig);
        this.metadata = metadata;
    }
 
    /**
     * Returns all accounts in the file
     * 
     * @return
     */
    public List<FlatFileUserAccount> getAllAccounts() {
        try {
            BufferedReader userRecordReader = new BufferedReader(
                    new FileReader(recordsStore.getAbsolutePath()));
            String recordStr;
 
            // Skip headers
            userRecordReader.readLine();
 
            // Loop over records and make list of objects
            List<FlatFileUserAccount> allAccountRecords = new ArrayList<FlatFileUserAccount>();
            while ((recordStr = userRecordReader.readLine()) != null) {
                try {
                    FlatFileUserAccount accountRecord = accountConverter
                            .convertStringRecordToAccountObj(recordStr);
                    allAccountRecords.add(accountRecord);
                } catch (RuntimeException e) {
                    System.out.println("Invalid entry " + e.getMessage());
                }
            }
            userRecordReader.close();
 
            return allAccountRecords;
        } catch (IOException e) {
            throw new RuntimeException("How can I read a corrupted file");
        }
    }
 
    /**
     * Gets the account of matching account identifier
     * 
     * @param accountIdentifier
     * @return
     */
    public FlatFileUserAccount getAccount(String accountIdentifier) {
 
        /*
         * I know its not right to get all account details. Don't want to focus
         * on efficiency and scalability as this is just a sample.
         */
        // Iterate over all records and check for matching account
        Map<String, String> matchSet = new HashMap<String, String>();
        matchSet.put(fileConfig.getUniqueAttributeName(), accountIdentifier);
        for (FlatFileUserAccount userRecord : getAllAccounts()) {
            if (userRecord.hasMatchingAttributes(matchSet))
                return userRecord;
        }
 
        // Got nothing..
        return null;
    }
 
    /**
     * Returns all records with matching Attributes If more than attributes are
     * passed. it will check all the attributes
     * 
     * @param matchSet
     *            Checks if all provided attributes are matched
     */
    public List<FlatFileUserAccount> getAccountsByMatchedAttrs(
            Map<String, String> matchSet) {
        /*
         * I know its not right to get all account details. Don't want to focus
         * on efficiency and scalability as this is just a sample.
         */
        // Iterate over all records and check for matching account
        List<FlatFileUserAccount> matchingRecords = new ArrayList<FlatFileUserAccount>();
        for (FlatFileUserAccount userRecord : getAllAccounts()) {
            if (userRecord.hasMatchingAttributes(matchSet))
                matchingRecords.add(userRecord);
        }
 
        return matchingRecords;
    }
 
    /**
     * Returns the records that fall after the specified change number This
     * function helps in checking the function of sync
     * 
     * @param changeNumber
     *            the change number for the last search
     */
    public List<FlatFileUserAccount> getUpdatedAccounts(int changeNumber) {
        /*
         * I know its not right to get all account details. Don't want to focus
         * on efficiency and scalability as this is just a sample.
         */
        // Iterate over all records and check for matching account
        List<FlatFileUserAccount> matchingRecords = new ArrayList<FlatFileUserAccount>();
        String changeLogAttrName = fileConfig.getChangeLogAttributeName();
        for (FlatFileUserAccount userRecord : getAllAccounts()) {
            int recordChangeNumber = userRecord
                    .getChangeNumber(changeLogAttrName);
            if (recordChangeNumber >= changeNumber)
                matchingRecords.add(userRecord);
        }
        return matchingRecords;
 
    }
 
    /**
     * Returns an iterator that iterates over the records. This is provided for
     * dynamic retrieval of records
     * 
     * @param matchSet
     *            Filters the records by matching the given attributes. Use null
     *            or empty set to avoid filtering
     * @return
     */
    public Iterator<FlatFileUserAccount> getAccountIterator(
            Map<String, String> matchSet) {
        Iterator<FlatFileUserAccount> recordIterator = new FlatFileLineIterator(
                this.metadata, this.fileConfig, matchSet);
 
        return recordIterator;
    }
 
    /**
     * Gives the next change number. Logic is max of existing change numbers + 1
     * @return
     */
    public int getNextChangeNumber() {
        int maximumChangeNumber = 0;
 
        /*
         * I know its not right to get all account details. Don't want to focus
         * on efficiency and scalability as this is just a sample.
         */
        // Iterate over all records and check for matching account
        String changeLogAttrName = fileConfig.getChangeLogAttributeName();
        for (FlatFileUserAccount userRecord : getAllAccounts()) {
            int changeNumber = userRecord.getChangeNumber(changeLogAttrName);
 
            if (changeNumber >= maximumChangeNumber) {
                maximumChangeNumber = changeNumber + 1;
            }
        }
        return maximumChangeNumber;
    }
}

5.3.4 Implementation of the FlatFileWriter Supporting Class

The following code sample shows the implementation of the FlatFileWriter supporting class:

package org.identityconnectors.flatfile.io;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
import org.identityconnectors.flatfile.FlatFileConfiguration;
import org.identityconnectors.flatfile.FlatFileUserAccount;
import org.identityconnectors.flatfile.utils.AccountConversionHandler;
 
/**
 * Class for searching operations on files
 * 
 * @author Harsh
 */
public class FlatFileWriter {
 
    private File recordsStore;
    private FlatFileParser recordParser;
    private FlatFileConfiguration fileConfig;
    private AccountConversionHandler accountConverter;
 
    /**
     * Initializes the writer with the configuration Making it package private
     * to encourage use of Factory class for global instantiation
     * 
     * @param metadata
     * @param fileConfig
     */
    FlatFileWriter(FlatFileMetadata metadata, FlatFileConfiguration fileConfig) {
        this.fileConfig = fileConfig;
 
        this.recordsStore = fileConfig.getStoreFile();
        recordParser = new FlatFileParser(metadata, fileConfig);
        accountConverter = new AccountConversionHandler(metadata, fileConfig);
    }
 
    /**
     * Appends the user record at the end of
     * 
     * @param accountRecord
     */
    public void addAccount(FlatFileUserAccount accountRecord) {
        try {
            BufferedWriter userRecordWriter = new BufferedWriter(
                    new FileWriter(this.recordsStore.getAbsolutePath(), true));
 
            // Set the latest changelog number
            int latestChangeNumber = recordParser.getNextChangeNumber();
            accountRecord.setChangeNumber(fileConfig
                    .getChangeLogAttributeName(), latestChangeNumber);
 
            // Validate if same account id doesn't exist
            String accountUid = accountRecord.getAttributeValue(fileConfig
                    .getUniqueAttributeName());
            FlatFileUserAccount accountByAccountId = recordParser
                    .getAccount(accountUid);
 
            if (accountByAccountId != null)
                throw new RuntimeException("Account " + accountUid
                        + " already exists");
 
            // Put the user record in formatted way
            String userRecordAsStr = accountConverter
                    .convertAccountObjToStringRecord(accountRecord);
            userRecordWriter.write("\n" + userRecordAsStr);
 
            // Close the output stream
            userRecordWriter.close();
        } catch (IOException e) {// Catch exception if any
            throw new RuntimeException("How can I write on a corrupted file");
        }
    }
 
    /**
     * Removes the entry for respective account identifier
     * 
     * @param accountIdentifier
     */
    public void deleteAccount(String accountIdentifier) {
        String blankRecord = "";
        this.modifyAccountInStore(accountIdentifier, blankRecord);
    }
 
    /**
     * Updates the entry with respective account identifier
     * 
     * @param accountIdentifier
     * @param updatedAccountRecord
     * @return new accountIdentifier
     */
    public String modifyAccount(String accountIdentifier,
            FlatFileUserAccount updatedAccountRecord) {
 
        // Frame a record string and update back to file
        int nextChangeNumber = recordParser.getNextChangeNumber();
 
        String changeNumberFieldName = fileConfig.getChangeLogAttributeName();
        updatedAccountRecord.setChangeNumber(changeNumberFieldName,
                nextChangeNumber);
 
        String newRecordAsStr = accountConverter
                .convertAccountObjToStringRecord(updatedAccountRecord);
        // Update to the file
        this.modifyAccountInStore(accountIdentifier, newRecordAsStr);
 
        // Return new UID
        String uniqueAttrFieldName = fileConfig.getUniqueAttributeName();
        String newAccountIdentifier = updatedAccountRecord
                .getAttributeValue(uniqueAttrFieldName);
        return newAccountIdentifier;
    }
 
    /**
     * Returns the complete flat file as string.
     * 
     * @return
     */
    private String getCompleteFlatFileAsStr() {
        try {
            BufferedReader userRecordReader = new BufferedReader(
                    new FileReader(recordsStore.getAbsolutePath()));
            String recordStr;
 
            // Loop over records and make list of objects
            StringBuilder flatFileStr = new StringBuilder();
            while ((recordStr = userRecordReader.readLine()) != null) {
                if (!recordStr.isEmpty())
                    flatFileStr.append(recordStr + "\n");
            }
            userRecordReader.close();
 
            return flatFileStr.toString();
        } catch (IOException e) {
            throw new RuntimeException("How can I read a corrupted file");
        }
    }
 
    /**
     * Updates the account with the new record. this can also be used for delete
     * 
     * @param accountIdentifier
     * @param updatedRecord
     */
    private void modifyAccountInStore(String accountIdentifier,
            String updatedRecord) {
        try {
            // Load the complete flat file
            String completeFlatFile = this.getCompleteFlatFileAsStr();
 
            // Construct the string to be removed and replace it with blank
            FlatFileUserAccount accountToBeRemoved = recordParser
                    .getAccount(accountIdentifier);
            String updatableString = accountConverter
                    .convertAccountObjToStringRecord(accountToBeRemoved);
            String updatedFlatFile = completeFlatFile.replaceAll(
                    updatableString, updatedRecord);
 
            // Rewrite the file
            BufferedWriter userRecordWriter = new BufferedWriter(
                    new FileWriter(this.recordsStore.getAbsolutePath(), false));
            userRecordWriter.write(updatedFlatFile);
 
            /*** debug ***/
            System.out.println("Old string " + updatableString);
            System.out.println("New String" + updatedRecord);
            System.out.println("new file - " + updatedFlatFile);
 
            /******/
            // Close the output stream
            userRecordWriter.close();
        } catch (IOException e) {// Catch exception if any
            throw new RuntimeException("How can I write on a corrupted file");
        }
    }
}

5.3.5 Implementation of the FlatfileLineIterator Supporting Class

The following code sample shows the implementation of the FlatfileLineIterator supporting class:

package org.identityconnectors.flatfile.io;
 .
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.Map;
 .
 import org.identityconnectors.flatfile.FlatFileConfiguration;
 import org.identityconnectors.flatfile.FlatFileUserAccount;
 import org.identityconnectors.flatfile.utils.AccountConversionHandler;
 .
 /**
  * Iterator class to fetch the records dynamically during search operations
 This
  * is needed to prevent VM overloading when all records are stored in memory
  *
  * @author admin
  *
  */
 public class FlatFileLineIterator implements Iterator<FlatFileUserAccount> {
 .
     private File recordsStore;
     private AccountConversionHandler accountConverter;
     private FlatFileUserAccount nextRecord;
     private BufferedReader userRecordReader;
     private Map<String, String> attrConstraints;
 .
     /**
      * Making it package private to prevent global initialization
      *
      * @param metadata
      * @param fileConfig
      * @param attributeValConstraints
      *            Iterator will apply this constraint and filter the result
      */
     FlatFileLineIterator(FlatFileMetadata metadata,
             FlatFileConfiguration fileConfig,
             Map<String, String> attributeValConstraints) {
         this.recordsStore = fileConfig.getStoreFile();
         this.accountConverter = new AccountConversionHandler(metadata,
                 fileConfig);
         this.attrConstraints = attributeValConstraints;
 .
         initializeReader();
         this.nextRecord = readNextValidRecord();
     }
 .
     private void initializeReader() {
         try {
             userRecordReader = new BufferedReader(new FileReader(recordsStore
                     .getAbsolutePath()));
 .
             // Skip headers
             userRecordReader.readLine();
 .
         } catch (IOException io) {
             throw new IllegalStateException("Unable to read "
                     + recordsStore.getName());
         }
     }
 .
     @Override
     public boolean hasNext() {
         return (nextRecord != null);
     }
 .
     @Override
     public FlatFileUserAccount next() {
         FlatFileUserAccount currentRecord = this.nextRecord;
         this.nextRecord = readNextValidRecord();
         return currentRecord;
     }
 .
     @Override
     public void remove() {
         // Nothing to do here
     }
 .
     /**
      * Returns next valid record. This happens after applying
      *
      * @return
      */
     private FlatFileUserAccount readNextValidRecord() {
         try {
             FlatFileUserAccount userAccObj = null;
             String recordStr;
             // Match the constraints or read next line
             do {
                 System.out.println("Before record string");
                 recordStr = getNextLine();
 .
                 // No more records ??
                 if (recordStr == null)
                     return null;
 .
                 userAccObj = accountConverter
                         .convertStringRecordToAccountObj(recordStr);
             } while (!userAccObj.hasMatchingAttributes(attrConstraints));
            
             return userAccObj;
         } catch (Exception e) {
             System.out.println("Error reading record" + e.getMessage());
             e.printStackTrace();
             return null;
         }
     }
.
     private String getNextLine() throws IOException {
         String nextLine = userRecordReader.readLine();
 .
         // No records ??
         if (nextLine == null) {
             this.userRecordReader.close();
             return null;
         }
 .
         if (nextLine.trim().isEmpty()) {
             return getNextLine();
         }
 .
         return nextLine;
     }
 }
 

5.3.6 Implementation of the FlatfileUserAccount Supporting Class

The following code sample shows the implmentation of the FlatfileUserAccount supporting class:

 package org.identityconnectors.flatfile;
 .
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 .
 import org.identityconnectors.framework.common.objects.Attribute;
 .
 /**
  * Object representing a user entity
  *
  * @author admin
  *
  */
 public class FlatFileUserAccount {
 .
     /*
      * Mandatory attribute names
      */
     private Set<String> mandatoryAttrNames = new HashSet<String>();
 .
     /*
      * Attributes making the account
      */
     private Map<String, String> attributes = new HashMap<String, String>();
 .
     /**
      * Instantiates the attribute value map
      *
      * @param mandatoryAttributeNames
      *            Names of the attributes that are necessary
      * @param attributeValMap
      *            Name value map for the attributes.
      * @throws IllegalStateException
      *             If mandatory attributes are not found in attribute val map
      */
     public FlatFileUserAccount(Set<String> mandatoryAttributeNames,
             Map<String, String> attributeValMap) {
         // Check if mandatory attribute values are passed
         Set<String> attrValuesKeySet = attributeValMap.keySet();
         if (!attrValuesKeySet.containsAll(mandatoryAttributeNames))
             throw new IllegalStateException("Mandatory attributes missing");
 .
         // Initialize
         this.mandatoryAttrNames = mandatoryAttributeNames;
         this.attributes = attributeValMap;
 .
     }
    
     /**
      * Instantiates the attribute value map.
      * Considers all attributes to be mandatory
      * @param attributeValMap
      */
     public FlatFileUserAccount(Map<String, String> attributeValMap) {
         this.mandatoryAttrNames = attributeValMap.keySet();
         this.attributes = attributeValMap;
     }
    
     /**
      * Instantiates the attribute value map
      * @param attrs
      */
     public FlatFileUserAccount(Set<Attribute> attrs) {
     for(Attribute attr: attrs) {
     String attrName = attr.getName();
    
     //Consider first value. Multivalued not supported
     String attrVal = (String) attr.getValue().get(0);
     this.attributes.put(attrName, attrVal);
     }
     }
 .
     /**
      * Updates the set of attributes. If new attributes present, they are
 added,
      * If old attributes are present in the parameter set, values are updated
      *
      * @param updatedAttributeValMap
      */
     public void updateAttributes(Map<String, String> updatedAttributeValMap)
 {
         this.attributes.putAll(updatedAttributeValMap);
     }
    
     /**
      * Updates the set of attributes.
      * @param upatedAttributes
      */
     public void updateAttributes(Set<Attribute> upatedAttributes) {
     Map<String, String> updatedAttributeValMap = new HashMap<String,
 String>();
     for(Attribute attr: upatedAttributes) {
     String attrName = attr.getName();
    
     //Consider first value. Multivalued not supported
     String attrVal = (String) attr.getValue().get(0);
     updatedAttributeValMap.put(attrName, attrVal);
     }
     this.attributes.putAll(updatedAttributeValMap);
     }
.
     /**
      * Deletes the attributes with given name.
      *
      * @param attributeKeys
      *            Set of the attribute names that are needed
      * @throws UnsupportedOperationException
      *             if delete for mandatory attributes is attempted
      */
     public void deleteAttributes(Set<String> attributeKeys) {
         // Check if mandatory attributes are not there.
         for (String attrKey : attributeKeys) {
             if (this.mandatoryAttrNames.contains(attrKey))
                 throw new UnsupportedOperationException(
                         "Delete for mandatory attributes not supported. Try
 update");
             // Not deleting here as it might result inconsistent
         }
         // Remove the attributes
         for (String attrKey : attributeKeys) {
             this.attributes.remove(attrKey);
         }
     }
 .
     /**
      * Gets the attribute of a given name
      *
      * @param attributeName
      * @return
      * @throws IllegalArgumentException
      *             if attribute is not there for a given name
      */
     public String getAttributeValue(String attributeName) {
         return this.attributes.get(attributeName);
     }
 .
     /**
      * Returns the current set of attributes
      *
      * @return
      */
     public Map<String, String> getAllAttributes() {
         return this.attributes;
     }
 .
     /**
      * Returns true if all passed attributes are matching for this object
      *
      * @param attrValMap
      * @return
      */
     public boolean hasMatchingAttributes(Map<String, String> attrValMap) {
         boolean noFilterSupplied = (attrValMap == null )||
 (attrValMap.isEmpty());
         if (noFilterSupplied)
             // No filter. Everything matches
             return true;
        
         // Iterate to match attributes one by one
         Set<String> keySet = attrValMap.keySet();
         for (String attrName : keySet) {
             String objAttrVal = this.attributes.get(attrName);
             String passedValue = attrValMap.get(attrName);
 .
             if (!objAttrVal.equals(passedValue))
                 // This attribute is not same
                 return false;
         }
 .
         // All attributes are same
         return true;
     }
 .
     /**
      * Returns the change log number
      *
      * @param changeLogAttrName
      *            attribute representing the number
      * @return
      */
     public int getChangeNumber(String changeLogAttrName) {
         String changeNumStr = this.attributes.get(changeLogAttrName);
         int changeNumber = 0;
 .
         try {
             changeNumber = Integer.parseInt(changeNumStr);
         } catch (Exception e) {
             System.out.println("Not a valid change log number "
                     + changeLogAttrName + " :" + changeNumStr);
         }
 .
         return changeNumber;
     }
    
     /**
      * Sets the given attribute with a new value
      * @param attrName
      * @param attrVal
      */
     public void setAttribute(String attrName, String attrVal) {
         this.attributes.put(attrName, attrVal);
     }
    
     /**
      * Updates the changelog number
      * @param changeLogAttrName
      * @param newChangeNumber
      */
     public void setChangeNumber(String changeLogAttrName, int
 newChangeNumber) {
         String changeNumberValStr = "" + newChangeNumber;
         this.attributes.put(changeLogAttrName, changeNumberValStr);
     }
 .
     @Override
     public String toString() {
         // Just print the attributes
         return this.attributes.toString();
     }
 .
 }

5.3.7 Implementation of the FlatfileAccountConversionHandler Supporting Class

This following code sample shows the implementation of the FlatfileAccountConversionHandler supporting class:

 package org.identityconnectors.flatfile.utils;
 .
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 .
 import org.identityconnectors.flatfile.FlatFileConfiguration;
 import org.identityconnectors.flatfile.FlatFileUserAccount;
 import org.identityconnectors.flatfile.io.FlatFileMetadata;
 .
 /**
  * Class for the utility functions
  *
  * @author Admin
  *
  */
 public class AccountConversionHandler {
 .
     private FlatFileConfiguration fileConfig;
     private FlatFileMetadata metadata;
 .
     /**
      * Instantiates the handler class. But needs the configuration
      *
      * @param metadata
      * @param fileConfig
      */
     public AccountConversionHandler(FlatFileMetadata metadata,
             FlatFileConfiguration fileConfig) {
         this.fileConfig = fileConfig;
         this.metadata = metadata;
     }
 .
     /**
      * Converts strings records to the user account objects.
      *
      * @param accountRecord
      * @return
      * @throws RuntimeException
      *             If string is not formatted as per accepted standards
      */
    public FlatFileUserAccount convertStringRecordToAccountObj(
             String accountRecord) {
 .
         StringTokenizer tokenizer = new StringTokenizer(accountRecord,
                 fileConfig.getTextFieldDelimeter());
 .
         // Assert number of columns matching with number of tokens
         if (metadata.isDifferentFromNumberOfFields(tokenizer.countTokens()))
             throw new RuntimeException(
                     "Number of tokens doesn't match number of columns");
 .
         // Get the attributes
         List<String> attrNames = metadata.getOrderedTextFieldNames();
         Map<String, String> attrValMap = new HashMap<String, String>();
 .
         // Number of tokens are same. Same loop will work
         for (String attrName : attrNames) {
             String attrVal = "";
             if (tokenizer.hasMoreTokens())
                 attrVal = tokenizer.nextToken();
 .
             attrValMap.put(attrName, attrVal);
         }
 .
         // Assumption : All attributes are mandatory for user. Change with
 the
         // change in assumption
         Set<String> mandatoryAttributeNames = attrValMap.keySet();
         FlatFileUserAccount userAccountRecordObj = new FlatFileUserAccount(
                 mandatoryAttributeNames, attrValMap);
         return userAccountRecordObj;
 .
    }
 .
     /**
      * Converts account objects to storable string records
      *
      * @param accountObj
      * @return
      */
     public String convertAccountObjToStringRecord(
             FlatFileUserAccount accountObj) {
         StringBuilder strRecord = new StringBuilder();
 .
         // Build the string record from the object
         List<String> attrNames = metadata.getOrderedTextFieldNames();
        
         int index=0;
         for (String attrName: attrNames) {
             String attrVal = accountObj.getAttributeValue(attrName);
             strRecord.append(attrVal);
           
            // Add delimeter
             if (index < attrNames.size()-1) {
                 strRecord.append(fileConfig.getTextFieldDelimeter());
                 index++;
             } else {
                 // Record ended
                 String newLineCharacter = "\n";
                 strRecord.append(newLineCharacter);
                 break;
             }
         }
         return strRecord.toString();
     }
 .
     /**
      * Asserts if given object is not null
      *
      * @param message
      * @param obj
      */
     public void assertNotNull(String message, Object obj) {
         if (obj == null)
             throw new RuntimeException(message);
     }
    
 }

5.3.8 Implementation of the Messages.Properties Supporting Class

The following code sample shows the implementation of the Messages.Properties supporting class:

USER_ACCOUNT_STORE_HELP=File in which user account will be stored
USER_ACCOUNT_STORE_DISPLAY=User Account File
USER_STORE_TEXT_DELIM_HELP=Text delimeter used for separating the columns
USER_STORE_TEXT_DELIM_DISPLAY=Text Field Delimeter
UNIQUE_ATTR_HELP=The name of the attribute which will act as unique identifier
UNIQUE_ATTR_DISPLAY=Unique Field
CHANGELOG_ATTR_HELP=The name of the attribute which will act as changelog
CHANGELOG_ATTR_DISPLAY=Changelog Field

5.4 Uploading the Identity Connector Bundle to Oracle Identity Governance Database

The identity connector bundle must be available to ICF in Oracle Identity Governance database.

Follow the list of sections in order to integrate the ICF identity connector with Oracle Identity Manager. Some of the procedures include configuration by using the Oracle Identity Manager Design Console.

5.4.1 Registering the Connector Bundle with Oracle Identity Governance

The connector bundle must be available for the Connector Server local to Oracle Identity Manager.

Following is the procedure to accomplish this:

  1. Copy the connector bundle JAR to the machine on which Oracle Identity Manager in installed.
  2. Run the following command to upload the JAR.

    $MW_HOME/server/bin/UploadJars.sh

    Note:

    In this chapter, DW_HOME represents $MW_HOME/Oracle_IDM1.

  3. Select ICFBundle as the JAR type.
  4. Enter the location of the connector bundle JAR.
  5. Press Enter.

5.4.2 Creating Basic Identity Connector Metadata

The connector metadata configuration is needed for both provisioning and reconciliation.

The following set of procedures in this section are completed by using the Oracle Identity Manager Design Console:

5.4.2.1 Creating the IT Resource Type Definition

An IT resource type definition is the representation of a resource's connection information. The configuration parameters in the IT resource type definition should be matched with the configuration parameters of the connector bundle. The values of the parameters in the IT resource will be set in the bundle configuration.

Note:

You may include parameters the bundle configuration is not using. They produce no negative effects on the bundle operations.

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click IT Resource Type Definition under Resource Management.
  3. Create a new IT Resource Type Definition with the Server Type defined as Flat File.
  4. Add the following parameters as illustrated in Figure 5-1.
    • Configuration Lookup is the marker of the main configuration lookup for the resource. The name of the parameter must be Configuration Lookup. It is a good practice to add a value to Default Field Value.

    • textFieldDelimeter maps to the textFieldDelimeter parameter in the bundle configuration. The value of this parameter will be passed.

    • storeFile maps to the storeFile parameter in the bundle configuration. The value of this parameter will be passed.

    Figure 5-1 IT Resource Type Definition in Design Console

    Description of Figure 5-1 follows
    Description of "Figure 5-1 IT Resource Type Definition in Design Console"
5.4.2.2 Creating the Resource Object

The resource object is the Oracle Identity Manager representation of a resource. The connector bundle is tied to the resource object.

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Resource Objects under Resource Management.
  3. Create a new resource object with the name FLATFILERO.

    As the resource object is a target resource don't check the Trusted Source box as illustrated in Figure 5-2.

    Figure 5-2 Resource Objects in Design Console

    Description of Figure 5-2 follows
    Description of "Figure 5-2 Resource Objects in Design Console"
5.4.2.3 Creating Lookups

Separate lookups have to be defined for different objects supported by the connector bundle. This lookup can contain provisioning and reconciliation related information for those objects. The Main Configuration Lookup is the root for object specific lookups as it contains the pointers to those lookups. The following sections contain information on how to create lookups.

5.4.2.3.1 Creating the Main Configuration Lookup

The Configuration Lookup (as defined in Creating the IT Resource Type Definition) holds connector bundle configurations that are not counted as connection information. If a configuration parameter is not found in the IT Resource Type Definition, Oracle Identity Manager will look in the Configuration Lookup. The main Configuration Lookup contains bundle properties and bundle configurations. Bundle Property parameters are mandatory as they are needed for identifying the correct bundle. Bundle configurations that are not defined as part of the IT resource type definition (discussed in Creating the IT Resource Type Definition) can be declared here.

Note:

The values for Code Key should match exactly as illustrated. The values for Decode are specific to the connector bundle.

To create the main configuration lookup:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Lookup Definition under Administration.
  3. Create a new lookup and add Lookup.FF.Configuration as the value for Code.
  4. Add the following Lookup Code Information as illustrated in Figure 5-3.
    • Add VERSION as the required Bundle Version.

    • Add org.identityconnectors.flatfile as the required Bundle Name.

    • Add org.identityconnectors.flatfile.FlatFileConnector as the required Connector Name.

    • Add AccountId as the value of uniqueAttributeName. AccountId is a unique string identifier that represents the account to be provisioned or reconciled. It is the name of the column in the flat file. AccountId is unique and is used to represent a user (account detail) uniquely.

    • Add ChangeNumber as the value of changeLogAttributeName. When an account is created, a number is attached to it indicating the total accounts created. This value is maintained in the variable called ChangeNumber.

    • OBJECT_TYPE_NAME Configuration Lookup is the configuration lookup for the particular object type. In this example, the object type is User as User Configuration Lookup is defined.

    Figure 5-3 Lookup Definition in Design Console

    Description of Figure 5-3 follows
    Description of "Figure 5-3 Lookup Definition in Design Console"
5.4.2.3.2 Creating Object Type Configuration Lookup

Object type configuration lookup contains the parameters specific to the particular object type. Object type is an entity over which an identity connector operates. It is mapped to ICF ObjectClass. In Creating the Main Configuration Lookup, User Configuration Lookup has been referenced so that User is the object type, in this case mapped to ObjectClass.ACCOUNT. (Roles and UserJobData are two other object types.) The object type name has to match with ObjectClass name supported by the identity connector bundle. The User object type is mapped to predefined ObjectClass.ACCOUNT, the Group object type is mapped to predefined ObjectClass.GROUP. If the identity connector supports multiple objects, then this step must be repeated for each.

Note:

Because these use cases cover only the basic functionality, the configuration is kept to the mandatory attribute.

To create the object type configuration lookup:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Lookup Definition under Administration.
  3. Create a new Lookup and add Lookup.FF.UM.Configuration as the Code.
  4. Set the following attributes as illustrated in Figure 5-4.

    Note:

    This tutorial focuses on the minimum configurations needed to run an identity connector.

    • Provisioning Attribute Map takes a value of Lookup.FF.UM.ProvAttrMap. This lookup contains the mapping between Oracle Identity Manager fields and identity connector attributes. The mapping is used during provisioning.

    • Reconciliation Attribute Map takes a value of Lookup.FF.UM.ReconAttributeMap. This lookup contains the mapping between Oracle Identity Manager reconciliation fields and identity connector attributes. The mapping is used during reconciliation.

      Figure 5-4 Second Lookup Definition in Design Console

      Description of Figure 5-4 follows
      Description of "Figure 5-4 Second Lookup Definition in Design Console"

5.4.3 Creating Provisioning Metadata

To configure Oracle Identity Manager for flat file provisioning, you create the provisioning metadata, which involves creating a process form, adapters, a process definition, and a provisioning attribute mapping lookup.

The following sections should be followed in order to configure Oracle Identity Manager for flat file provisioning.

5.4.3.1 Creating a Process Form

A process form is used as the representation of object attributes on Oracle Identity Manager.

This section describes about process forms and how to create a process form. It contains the following topics:

5.4.3.1.1 Creating a Process Form

To create a process form:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Form Designer under Development Tools.
  3. Create a new form with the Table Name UD_FLAT_FIL as illustrated in Figure 5-5.

    Figure 5-5 Form Designer in Design Console

    Description of Figure 5-5 follows
    Description of "Figure 5-5 Form Designer in Design Console"
  4. Add the attributes defined in the connector schema, as listed in Attributes in the Connector Schema.
  5. Click the Properties tab.
  6. Add the following properties to Server(ITResourceLookupField) as illustrated in Figure 5-6.
    • Required = true

    • Type = Flat File

    Figure 5-6 Properties of Form Designer in Design Console

    Description of Figure 5-6 follows
    Description of "Figure 5-6 Properties of Form Designer in Design Console"
  7. Save the form.
  8. Click Make Version Active.
5.4.3.1.2 About Process Forms

A process form is used as the representation of object attributes on Oracle Identity Manager. This facilitates user input to set object attributes before passed to the connector bundle for an operation.

Attributes defined in the process form are not conventions. The form is a way to challenge the attributes that need to be passed to the identity connector. In general, define an attribute for each supported attribute in the identity connector.

Note:

It is good practice to have a one to one mapping on the identity connector attributes.

There should be a field for querying the IT resource that should be associated with the respective IT Resource Type Definition. Variable type of each field should map to the type of the object attribute.

5.4.3.1.3 Attributes in the Connector Schema

Table 5-1 lists the attributes defined in the connector schema.

Table 5-1 Form Designer Fields

Name Variant Field Label Field Type

UD_FLAT_FIL_FIRSTNAME

String

First Name

TextField

UD_FLAT_FIL_UID

String

Universal ID

TextField

UD_FLAT_FIL_CHANGENO

String

Change Number

TextField

UD_FLAT_FIL_MAILID

String

Email ID

TextField

UD_FLAT_FIL_SERVER

long

Server

ITResource

UD_FLAT_FIL_LASTNAME

String

Last Name

TextField

UD_FLAT_FIL_ACCOUNTID

String

Account ID

TextField

UD_FLAT_FIL_RETURN

String

Return ID

TextField

Note:

The flat file column names are FirstName, ChangeNo, EmailID, Server, LastName, and AccountID.

5.4.3.2 Creating Adapters

An adapter has to be created for all operations supported by the connector bundle, including Create, Update, and Delete.

To create the adapter:

  1. Log in to the Oracle Identity Manager Design Console.

  2. Click Adapter Factory under Development Tools.

  3. Create a new adapter and add FFCreateUser as the Adapter Name.

  4. Add Process Task as the Adapter Type.

  5. Save the adapter.

  6. Click the Variable List tab and add the following variables, as shown in Figure 5-7.

    • objectType with Type String and Mapped as Resolve at runtime.

    • processInstanceKey with Type long and Mapped as Resolve at runtime.

    • itResourceFieldName with Type String and Mapped as Resolve at runtime.

    Figure 5-7 Adapter Factory Variable List in Design Console

    Description of Figure 5-7 follows
    Description of "Figure 5-7 Adapter Factory Variable List in Design Console"
  7. Add a Java functional task to the adapter by following this sub procedure, as shown in Figure 5-8.

    1. Click the Adapter Tasks tab.

    2. Select the adapter and click Add.

    3. Select Java from the task options.

    4. Select icf-oim-intg.jar from the API source.

    5. Select oracle.iam.connetors.icfcommon.prov.ICProvisioninManager as the API Source.

    6. Select createObject as the method for the task.

    7. Save the configurations.

    8. Map the variables (previously added to the Variables List) against the appropriate method inputs and outputs.

    9. Map the configuration parameters against the appropriate method inputs and outputs.

      Database Reference maps to Database Reference (Adapter References) and Return Variable maps to Return Variable (Adapter Variables).

    Figure 5-8 Adapter Factory in Design Console

    Description of Figure 5-8 follows
    Description of "Figure 5-8 Adapter Factory in Design Console"
  8. Save and build the adapter.

5.4.3.3 Creating A Process Definition

Process Definition defines the behavior of the connector bundle for a particular operation. Every operation has a corresponding task associated with it.

The following procedure will configure the process definition and integration of the process task for the Create operation:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Process Definition under the Process Management tab.
  3. Create a new process definition and name it Flat File as illustrated in Figure 5-9.

    Figure 5-9 Process Definition in Design Console

    Description of Figure 5-9 follows
    Description of "Figure 5-9 Process Definition in Design Console"
  4. Select Provisioning as the Type of process.
  5. Provide the resource Object Name for the identity connector; in this example, FLATFILERO.
  6. Provide the process form Table Name; in this example, UD_FLAT_FIL.
  7. Add a process task and name it Create User.
  8. Double click Create User to edit as illustrated in Figure 5-10.

    Figure 5-10 Editing Task Screen in Design Console

    Description of Figure 5-10 follows
    Description of "Figure 5-10 Editing Task Screen in Design Console"
  9. Click the Integration tab.
  10. Click Add and select the FFCreateUser adapter from the list as illustrated in Figure 5-11.

    The adapter will be available only after it is compiled.

    Figure 5-11 Integration Tab in Design Console

    Description of Figure 5-11 follows
    Description of "Figure 5-11 Integration Tab in Design Console"
  11. Map the variables as follows to set the response code returned by the identity connector.
    • Adapter Return Variable – Response Code

    • Object Type – [Literal:String] User (Name of the object type)

    • Process Instance Key – [Process Data] Process Instance

    • IT Resource Field Name – [Literal:String] UD_FLAT_FIL_SERVER (Form field name that contains the IT resource information)

  12. Click the Responses tab and configure the responses as illustrated in Figure 5-12.
    • UNKNOWN can be described as Unknown response received with a status of R (Rejected).

    • SUCCESS can be described as Operation completed with a status of C (Completed).

    • ERROR can be described as Error occurred with a status of R.

    Figure 5-12 Configure Responses in Design Console

    Description of Figure 5-12 follows
    Description of "Figure 5-12 Configure Responses in Design Console"
  13. Click the Task to Object Status Mapping tab.
  14. Update the Object Status to Provisioned for Status C, as shown in Figure 5-13:

    Figure 5-13 Task to Object Status Mapping

    Description of Figure 5-13 follows
    Description of "Figure 5-13 Task to Object Status Mapping"
  15. Save the process task.
5.4.3.4 Creating a Provisioning Attribute Mapping Lookup

Provisioning Attribute Mapping Lookup contains mappings of Oracle Identity Manager fields to identity connector bundle attributes.

This section describes the following topics about the Provisioning Attribute Mapping Lookup:

5.4.3.4.1 About Provisioning Attribute Mapping Lookup

Provisioning Attribute Mapping Lookup contains mappings of Oracle Identity Manager fields to identity connector bundle attributes. In the Provisioning Attribute Mapping Lookup:

  • Code keys are Field Labels of the process form.

  • Decodes are identity connector bundle attributes.

  • Child form attributes can be configured as embedded objects in inputs.

  • The identity connector's provisioning operation returns the UID in response. This can be set in a form field by coding it against the identity connector bundle attribute.

5.4.3.4.2 Creating a Provisioning Attribute Mapping Lookup

To create a Provisioning Attribute Mapping Lookup:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Lookup Definition under the Administration tab.
  3. Create a new lookup and name it Lookup.FF.UM.ProvAttrMap.

    The name of this lookup is referred from the object type configuration lookup. See Creating Object Type Configuration Lookup.

  4. Add the form Field Labels as the code keys and identity connector bundle attributes as the decode.
    • Return ID : __UID__

    • Account ID: AccountId

    • Change Number: ChangeNumber

    • First Name: FirstName

    • Last Name: LastName

    • Email ID: MailId

5.4.3.4.3 Field Flags Used in the Provisioning Attributes Map

For provisioning attributes mapping, the following field flags can be appended to the code key:

  • LOOKUP: This must be specified for all fields whose values are obtained by running a lookup reconciliation job. The values obtained from lookup reconciliation job have IT Resource Name/Key appended to it. Specifying this flag helps ICF integration to remove the appended value just before passing them onto the bundle. For example, the code key for a field with label Database whose value is obtained by running a lookup reconciliation job looks similar to Database[LOOKUP].

    Note:

    The LOOKUP flag can be specified for both Provisioning and Reconciliation Attribute Map. For provisioning, IT Resource Name/IT Resource Key prefix must be removed. For reconciliation, IT Resource Name/IT Resource Key prefix must be added.

  • IGNORE: This must be specified for all fields whose values are to be ignored and not sent to bundle. For example, the code key for a field with label Database whose value need not be sent to bundle looks similar to Database[IGNORE].

  • WRITEBACK: This must be specified for all fields whose values need to be written back into the process form right after the create or update operation. Adding this flag makes the ICF integration layer call ICF Get API to get values of attributes marked with the WRITEBACK flag. For example, the code key for a field with label Database whose value needs to be written back to the process form right after create/update looks similar to Database[WRITEBACK]. For this to work, the connector must implement the GetApiOp interface and provide an implementation for the ConnectorObject getObject(ObjectClass objClass,Uid uid,OperationOptions options) API. This API searches the target for the account whose Uid is equal to the passed in Uid, and builds a connector object containing all the attributes (and their values) that are to be written back to process form.

    Note:

    If the connector does not implement the GetApiOp interface, then the WRITEBACK flag does not work and an error is generated.

  • DATE: This must be specified for fields whose type need to be considered as Date, without which the values are considered as normal strings. For example, the code key for a field with label Today whose value needs to be displayed in the date format looks similar to Today[DATE].

  • PROVIDEONPSWDCHANGE: This must be specified for all fields that need to be provided to the bundle(target) when a password update happens. Some targets expect additional attributes to be specified on every password change. Specifying the PROVIDEONPSWDCHANGE flag, tells ICF integration to send all the extra fields or attributes whenever a password change is requested. For example, the code key for a field with label Extra Attribute Needed for Password Change whose value needs to be provided to bundle(target) while password update looks similar to Extra Attribute Needed for Password Change[PROVIDEONPSWDCHANGE].

5.4.4 Creating Reconciliation Metadata

You can configure the reconciliation of records from the flat file. You can use the target reconciliation as an example; trusted reconciliation can also be configured in a similar fashion.

Perform the procedures in the listed order.

5.4.4.1 Creating a Reconciliation Scheduled Task

By default, reconciliation uses a Search operation on the connector bundle. This operation is invoked with a scheduled task configured using Oracle Identity Manager. This procedure is comprised of the following subprocedures:

5.4.4.1.1 Defining the Scheduled Task

To define the scheduled task:

  1. Create a Deployment Manager XML file containing the scheduled task details as shown in the following example. Make sure to update database value to your database.
    <?xml version = '1.0' encoding = 'UTF-8'?>
    <xl-ddm-data version="2.0.1.0" user="XELSYSADM" database="jdbc:oracle:thin:@localhost:5524/estView.regress.rdbms.dev.mycompany.com" exported-date="1307546406635" description="FF">
    <scheduledTask repo-type="MDS" name="Flat File Connector User Reconciliation" mds-path="/db" mds-file="Flat File Connector User Reconciliation.xml">
        <completeXml>
            <scheduledTasks xmlns="http://xmlns.oracle.com/oim/scheduler">
                <task>
                <name>Flat File Connector User Reconciliation</name>
                <class>oracle.iam.connectors.icfcommon.recon.SearchReconTask</class>
                <description>Flat File Connector User Reconciliation</description>
                <retry>0</retry>
                <parameters>
                  <string-param required="false" encrypted="false" helpText="Filter">Filter</string-param>
                  <string-param required="false" encrypted="false" helpText="Incremental Recon Date Attribute">Incremental Recon Date Attribute</string-param>
                  <string-param required="false" encrypted="false" helpText="IT Resource Name">IT Resource Name</string-param>
                  <string-param required="false" encrypted="false" helpText="Object Type">Object Type</string-param>
                  <string-param required="false" encrypted="false" helpText="Latest Token">Latest Token</string-param>
                  <string-param required="false" encrypted="false" helpText="Resource Object Name">Resource Object Name</string-param>
                </parameters>
              </task>
            </scheduledTasks>
        </completeXml>
    </scheduledTask>
    </xl-ddm-data>
    
  2. Save the file as Flat File Connector User Reconciliation.xml.
  3. Login to Oracle Identity System Administration. Under System Management, click Import.
  4. Select the Flat File Connector User Reconciliation.xml file, and click Import.
  5. Complete the steps in the wizard.
5.4.4.1.2 Creating a Scheduled Task

This procedure explains how to create a scheduled task.

  1. Log in to the Oracle Identity Manager Advanced Administration.
  2. Click Scheduler under the System Management tab.
  3. Add a schedule task and add Flat File Connector User Reconciliation as the type as illustrated in Figure 5-14.

    Figure 5-14 The Scheduled Task Screen

    Description of Figure 5-14 follows
    Description of "Figure 5-14 The Scheduled Task Screen "
  4. Set the parameters as follows:
    • IT Resource Name takes a value of Flat File.

    • Resource Object Name takes a value of FLATFILERO.

    • Object Type takes a value of User.

  5. Click Apply.
5.4.4.2 Creating a Reconciliation Profile

A reconciliation profile defines the structure of the object attributes while reconciliation. The reconciliation profile should contain all the attributes that have reconciliation support.

To create a reconciliation profile:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Resource Objects under Resource Management.
  3. Open the FLATFILERO resource object.
  4. Click the Object Reconciliation tab as illustrated in Figure 5-15.

    Figure 5-15 Object Reconciliation in Design Console

    Description of Figure 5-15 follows
    Description of "Figure 5-15 Object Reconciliation in Design Console"
  5. Add following reconciliation fields:
    • First Name [String]

    • Universal ID [String]

    • Email ID [String]

    • IT Resource Name [String]

    • Last Name [String]

    • Account ID [String], Required

  6. Save the configuration.
5.4.4.3 Setting a Reconciliation Action Rule

A Reconciliation Action Rule defines the behavior of reconciliation. In this procedure, define the expected action when a match is found. This procedure assumes you are logged into the Oracle Identity Manager Design Console.

  1. Open the FLATFILERO resource object.
  2. Click the Object Reconciliation tab.
  3. Click the Reconciliation Action Rules tab in the right frame as illustrated in Figure 5-16.

    Figure 5-16 Reconciliation Action Rules in Design Console

    Description of Figure 5-16 follows
    Description of "Figure 5-16 Reconciliation Action Rules in Design Console"
  4. Add an action rule defined as One Process Match Found (Rule Condition) and Establish Link (Action).
  5. Add an action rule defined as One Entity Match Found (Rule Condition) and Establish Link (Action).
  6. Click Create Reconciliation Profile.
  7. Click Save.
5.4.4.4 Creating Reconciliation Mapping

The reconciliation mapping has to be done in the process definition. This is to map the supported reconciliation fields (from resource object) to the process form fields. This mapping is needed only for configuring target reconciliation.

To create a reconciliation mapping:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Click Process Definition under Process Management.
  3. Open the Flat File process definition.
  4. Click the Reconciliation Field Mappings tab as illustrated in Figure 5-17.

    Figure 5-17 Reconciliation Field Mapping in Design Console

    Description of Figure 5-17 follows
    Description of "Figure 5-17 Reconciliation Field Mapping in Design Console"
  5. Add mappings between the reconciliation profile fields and the process form fields.
    • First Name[String] = UD_FLAT_FIL_FIRSTNAME

    • Email ID[String] = UD_FLAT_FIL_MAILID

    • IT Resource Name[String] = UD_FLAT_FIL_SERVER

    • Last Name[String] = UD_FLAT_FIL_LASTNAME

    • Account ID [String] = UD_FLAT_FIL_ACCOUNTID <KEY>

      <KEY> sets Account ID as a key field.

  6. Save the configuration.
5.4.4.5 Field Flags Used in the Reconciliation Attributes Map

For reconciliation attributes mapping, the following field flags can be appended to the code key:

  • TRUSTED: This must be specified in the Recon Attribute Map for the field that represents the status of the account. This flag must be specified only for trusted reconciliation. If this is specified, then the status of the account is either Active or Disabled. Otherwise, the status is either Enabled or Disabled. For example, the code key for a field with label Status whose value needs to be either Active/Disabled must look similar to Status[TRUSTED].

  • DATE: In Recon Attribute Map, this must be specified for fields whose type need to be considered as Date. For example, the code key for a field with label Today whose value needs to be displayed in the date format must look similar to Today[DATE].

5.4.4.6 Defining a Reconciliation Matching Rule

A reconciliation matching rule defines the equation for calculating the user match.

To define a reconciliation matching rule:

  1. Log in to the Oracle Identity Manager Design Console.
  2. Open the Reconciliation Rules form under Development Tools.
  3. Click Add Rule.

    Figure 5-18 Adding Reconciliation Matching Rule

    Description of Figure 5-18 follows
    Description of "Figure 5-18 Adding Reconciliation Matching Rule"
  4. Select resource object FLATFILERO.
  5. Save and add the rule element.

    User Login from the user profile data equals the Account ID resource attribute.

  6. Save the rule.

5.5 Provisioning a Flat File Account

Provisioning a Flat File account involves creating an IT resource of type Flat File with IT resource and Lookup.FF.Configuration parameters.

The flat file connector is ready to work. Now, the user needs to log in to Oracle Identity Manager and create an IT resource (target) using the following procedure.

  • Create IT resource of type "Flat File".

  • Provide the IT resource parameters as appropriate.

  • Provide the configuration parameters in Lookup.FF.Configuration as appropriate.

5.6 Installing the Java Connector Server

  1. Download the Connector Server package (Connector_Server_122130_java.zip) from the Oracle Technology Network site at the following URL:
  2. Extract the contents of the Connector Server package and locate the connector_server_java-1.5.0.zip file.
  3. Create a directory where you want to install Java Connector Server. This will be CONNECTOR_SERVER_HOME.
  4. Extract the contents of the connector_server_java-1.5.0.zip file to CONNECTOR_SERVER_HOME directory.
  5. In the CONNECTOR_SERVER_HOME/conf/ConnectorServer.properties file, set the properties as required by your deployment.

    The following example snippet shows the ConnectorServer.properties shipped with Java Connector Server:

    .
    ##
    ## The port we are to run on
    ##connectorserver.port=8759
    ##
    ## The bundle directory in which to find the bundles
    ##connectorserver.bundleDir=bundles
    .
    ##
    ## The bundle directory in which to find any libraries needed by bundles at
    runtime
    ##connectorserver.libDir=lib
    .
    ##
    ## Set to true to use SSL.
    ## NOTE: Check also the following settings which are related to SSL:
    ## connectorserver.promptKeyStorePassword
    ## connectorserver.keyStore
    ## connectorserver.keyStoreType
    ## connectorserver.keyStorePasswordconnectorserver.usessl=false
    ##
    ## Protocol in use for SSL communication e.g. TLSv1, TLSv1.1, TLSv1.2
    ##
    connectorserver.protocol=TLSv1
    .
    ##
    ## If set to true the user is prompted for key store password at startup.
    ## If set to false the key store password needs to be set with-setKeyStorePassword command first.
    ##
    connectorserver.promptKeyStorePassword=true
    .
    ##
    ## Full path to key store.
    ##connectorserver.keyStore=/tmp/KeyStore.jks
    .
    ##
    ## KeyStore type
    ##
    #connectorserver.keyStoreType=JKS
    .
    ##
    ## Encrypted password. Set this by using the -setKeyStorePassword flag.
    ## It is used only if connectorserver.promptKeyStorePassword is set to false.
    ##connectorserver.keyStorePassword=
    .
    ##
    ## Optionally specify a specific address to bind to
    ##
    #connectorserver.ifaddress=localhost
    .
    ##
    ## Secure hash of the gateway key. Set this by using the
    ## -setKey flag
    ##connectorserver.key=lmA6bMfENJGlIDbfrVtklXFK32s\=
    .
    ##
    ## Use standard JDK logging
    ##
    connectorserver.loggerClass=org.identityconnectors.common.logging.impl.JDKLogger
    
  6. The CONNECTOR_SERVER_HOME/conf directory also contains the logging.properties file, which you can edit if required by your deployment.

    Note:

    The logging.properties file allows you to enable or disable logging and update the level information for log files. By default, logging is enabled and level is set to INFO.

5.7 Configuring the Java Connector Server with SSL for Oracle Identity Governance

You can configure SSL for Java Connector Server by providing the key store credentials in the ConnectorServer.properties file.

To do so:
  1. Create a keystore that will be used for SSL communication between Oracle Identity Manager and Java connector server. To do so:
    1. On the host on which Java connector server is installed, locate the JAVA home directory.
    2. From the JAVA home directory, run the following command to generate a keystore:
      $JAVA_HOME/jre/bin/keytool -genkey {-alias ALIAS} {-keyalg KEYALG} {-keysize KEYSIZE} {-sigalg SIGALG} [-dname DNAME] [-keypass KEYPASS] {-validity VAL_DAYS} {-storetype STORETYPE} {-keystore KEYSTORE} [-storepass STOREPASS]

      For example:

      /scratch/jdk1.8.0_131/jre/bin/keytool -genkey
      -aliasjavaconnectorserver
      -keyalg RSA
      -keysize 2048
      -sigalg SHA256withRSA
      -dname "CN=localhost, OU=Identity, O=Oracle Corporation,C=US"
      -keypass WEBLOGIC_PASSWORD
      -keystore javaconnectorserver.jks
      -storepass WEBLOGIC_PASSWORD
    3. Export the certificate of the newly generated keystore to a file by running the following keytool command:
      $JAVA_HOME/jre/bin/keytool -export {-alias ALIAS} {-file CERT_FILE} {-storetype STORETYPE} {-keystore KEYSTORE} [-storepass STOREPASS]

      For example:

      $JAVA_HOME/jre/bin/keytool -export -alias javaconnectorserver
         -file javaconnectorserver.cert
         -keypass WEBLOGIC_PASSWORD
         -keystore javaconnectorserver.jks
         -storepass WEBLOGIC_PASSWORD
    4. Copy the certificate of Java connector server keystore on the Oracle Identity Manager host. Import this certificate of Java connector server keystore into the trust store used in Oracle Identity Manager by running the following command:
      $JAVA_HOME/jre/bin/keytool -import {-alias ALIAS} {-file CERT_FILE} [-keypass KEYPASS] {-noprompt} {-trustcacerts} {-storetype STORETYPE} {-keystore KEYSTORE} [-storepass STOREPASS]

      If Oracle Identity Manager is using custom identity and custom trust, then import the following certificate in custom trust and Java standard trust.

      $JAVA_HOME/jre/bin/keytool -import -alias javaconnectorservertrust -trustcacerts -file /scratch/javaconnectorserver.cert -keystore DOMAIN_HOME/config/fmwconfig/CUSTOM_TRUST_KEYSTORE -storepass WEBLOGIC_PASSWORD

      If Oracle Identity Manager is using custom identity and Java standard trust, then import this certificate in Java standard trust.

      $JAVA_HOME/jre/bin/keytool -import -alias javaconnectorservertrust -trustcacerts -file /scratch/javaconnectorserver.cert -keystore JAVA_HOME/jre/lib/security/cacerts -storepass PASSWORD

      If Oracle Identity Manager is using Demo Identity and Demo Trust, then import the following certificate in DOMAIN_HOME/config/fmwconfig/default-keystore.jks file of Oracle Identity Manager and in Java standard trust.

      $JAVA_HOME/jre/bin/keytool -import -alias javaconnectorservertrust -trustcacerts -file /scratch/javaconnectorserver.cert -keystore DOMAIN_HOME/config/fmwconfig/default-keystore.jks -storepass WEBLOGIC_PASSWORD
  2. Provide the location of this Java Connector Server keystore in the $CONNECTOR_SERVER_HOME/conf/ConnectorServer.properties file:
    connectorserver.usessl=true
    connectorserver.keyStore={full path to your keystore file}
    connectorserver.keyStoreType=JKS (optionally you can set key store type, if
    not set JSK is used by default)
    
  3. Provide the password of this Java Connector Server keystore in the $CONNECTOR_SERVER_HOME/conf/ConnectorServer.properties file. You can do one of the following:
    • Set connectorserver.promptKeyStorePassword=false in ConnectorServer.properties and set the password as:

      cd $CONNECTOR_SERVER/bin

      For UNIX: connectorserver.sh /setKeyStorePassword thepassword

      For Windows: ConnectorServer.bat /setKeyStorePassword thepassword

      This command will set the encrypted value to connectorserver.keyStorePassword in ConnectorServer.properties.

      or

    • Prompt to enter the keystore password every time you start the connector server by setting connectorserver.promptKeyStorePassword=true in ConnectorServer.properties file.

  4. You can set the protocol for secure communication by setting the connectorserver.protocol property in $CONNECTOR_SERVER_HOME/conf/ConnectorServer.properties file as:
    ## Protocol in use for SSL communication e.g. TLSv1, TLSv1.1, TLSv1.2
    ##
    connectorserver.protocol=TLSv1

    Default value for this property is TLSv1 for TLS 1.0 protocol.

    Note:

    You can configure SSL between Java Connector Server and Target System. To do so:

    • Check for the JAVA_HOME folder path in the Java Connector Server machine.

    • Import target system certificate in Java standard trust store (<JAVA_HOME>/jre/lib/security/cacerts) of Java Connector Server machine using below command:

      keytool -import -alias oidstore -keystore JAVA_HOME/jre/lib/security/cacerts -file /scratch/cert/b64certificate.txt -storepass PASSWORD

      Where, oidstore is the alias, JAVA_HOME is the java home folder in the Java Connector Server machine, /scratch/cert/b64certificate.txt is the target system certification file, and PASSWORD is the password.

5.8 Configuring the Java Connector Server without SSL for Oracle Identity Governance

To configure the Java Connector Server without SSL:
  1. In the $CONNECTOR_SERVER_HOME/conf/ConnectorServer.properties file, set the connectorserver.key property by running the Java Connector Server with the /setKey option.

    For Java Connector Server on Windows, go to $CONNECTOR_SERVER_HOME\bin directory and find the ConnectorServer.bat script. Run the script:

    ./ ConnectorServer.bat /setKey <KEY>

    For Java Connector Server on Solaris and Linux, go to $CONNECTOR_SERVER_HOME\bin directory and find the ConnectorServer.sh script. Run the script:

    ./ ConnectorServer.sh /setKey <KEY>
  2. For all other properties, edit the ConnectorServer.properties file manually.

    See Installing the .NET Connector Server for an example snippet of the ConnectorServer.properties shipped with Java Connector Server.

5.9 Upgrading the Java Connector Server

In the 12.2.1.3.0 version of the Connector Server pack, you can select the protocol for SSL communication between Oracle Identity Manager and Java Connector Server by using the connectorserver.protocol property. The supported values of this property are TLSv1, TLSv1.1, and TLSv1.2. Here, TLSv1 denotes TLS 1.0 protocol, TLSv1.1 denotes TLS 1.1 protocol, and TLSv1.2 denotes TLS 1.2 protocol. The default value of this property is TLSv1, which denotes TLS 1.0 protocol.

To upgrade the Java Connector Server:

  1. Stop the connector server service.
  2. Create a backup of the directory on which Connector server is installed.
  3. Download the Connector Server package (Connector_Server_122130_java.zip) from the Oracle Technology Network site at the following URL:
  4. Extract the contents of the Connector Server package (Connector_Server_122130_java.zip) and locate the connector_server_java-1.5.0.zip file.
  5. Extract the contents of the connector_server_java-1.5.0.zip file in a directory.
  6. Copy the files in the connector_server_java-1.5.0/bin/ and connector_server_java-1.5.0/lib/ directories from the 12.2.1.3.0 Java Connector Server pack to the installed location of Java Connector Server.
  7. Open connector_server_java-1.5.0/conf/ConnectorServer.properties file from 12.2.1.3.0 Java Connector Server pack and open conf/ConnectorServer.properties file from installed location of Java Connector Server.
  8. Add the following section in the conf/ConnectorServer.properties file at installed location from connector_server_java-1.5.0/conf/ConnectorServer.properties file in 12.2.1.3.0 Java connector server pack:
    ## Protocol in use for SSL communication e.g. TLSv1, TLSv1.1, TLSv1.2
    connectorserver.protocol=TLSv1

    This property provides an option to select the protocol for SSL communication. By default, the value is TLS1.0.

    Note:

    Customizations are preserved during the upgrade of the Connector Server. If you have any customization in any of the updated files, then redo the same customizations form the backed up file.
  9. Start the Connector Server after doing all the required settings.