Skip Headers
Oracle® Fusion Middleware Developer's Guide for Oracle Identity Manager
11g Release 1 (11.1.1)

Part Number E14309-08
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

16 Understanding the Identity Connector Framework

Identity connectors are components developed to link Oracle Identity Manager with external stores of applications, directories, and databases. This release of Oracle Identity Manager provides support for developing and building identity connectors by using the Identity Connector Framework (ICF). Using the ICF decouples Oracle Identity Manager from the other applications to which it connects. Therefore, you can build and test an identity connector before integrating it with Oracle Identity Manager. This chapter contains conceptual information and sample code in the following sections:

Note:

Earlier releases of Oracle Identity Manager have other options for building identity connectors. These options are still supported, but it is recommended that you build new identity connectors by using the ICF.

16.1 Introducing the ICF Architecture

Identity connectors allow Oracle Identity Manager to carry out user provisioning and reconciliation operations on target systems in the enterprise. ICF decouples any calling application, such as Oracle Identity Manager, from the implementation of the connector. ICF also decouples the implementation of the connector from the calling application. The same connector implementation can work with several different calling applications. Figure 16-1 illustrates how this is accomplished by situating the ICF API and SPI between Oracle Identity Manager and the target system.

The API implementation always post-processes the results returned by the SPI Search operation. This double-checks the SPI implementation if the connector bundle does not implement all Filter types, or does not implement them properly for all attributes. If the implementation of Search in the SPI returns every object of the specified type, then the API implementation discards every object that does not match the specified Filter. Post-processing in the API implementation is expensive in terms of processing-time and network-bandwidth, and therefore, it is more efficient if each connector-bundle supports every type of filter (search predicate or logical operator) that the target application can support natively. See the details for Filter Translator in "Common Classes".

Figure 16-1 illustrates that the calling application sees only the ICF API. The ICF API dedicates a classloader to each connector bundle, so that the calling application is not exposed to the classes and libraries in the implementation of the connector-bundle (SPI). Bundle classloader also ensures isolation between the bundles as well as making any bundled library available to the connector bundle only, thereby avoiding conflicts between dependencies.

Figure 16-1 Identity Connector Framework Deployment

Description of Figure 16-1 follows
Description of "Figure 16-1 Identity Connector Framework Deployment"

Figure 16-2 illustrates the backwards compatibility of the ICF. Newer bundles may be deployed without affecting existing ones. In addition, newer versions of the ICF are generally backward-compatible with existing bundles.

Figure 16-2 Compatibility Between the ICF and Connector Bundles

Description of Figure 16-2 follows
Description of "Figure 16-2 Compatibility Between the ICF and Connector Bundles"

Identity connectors are stateless by design. An identity connector stores nothing. The calling application supplies to the connector the values for its configuration, including the information required to connect to the target application. Because identity connectors are stateless, each bundle implementation are kept as simple as possible, and coupling the implementation with that of the calling application is also prevented.

16.2 Using the ICF API

The org.identityconnectors.framework.api package contains the ICF API. Oracle Identity Manager uses the API to call Connector implementations. The API provides a consistent view of any implemented Connector, regardless of the supported operations. The following sections explain these interfaces and classes.

16.2.1 The ConnectorInfoManagerFactory Class

The ConnectorInfoManagerFactory class allows Oracle Identity Manager to load Connector classes from a set of bundles. The static getInstance method returns an object of type ConnectorInfoManagerFactory. This object can then be used to get a reference to the ConnectorInfoManager. (See Section 16.2.2, "The ConnectorInfoManager Interface" for more information.) Example 16-1 illustrates the ConnectorInfoManagerFactory implementation.

Example 16-1 ConnectorInfoManagerFactory Implementation

//create ConnectorInfoManagerFactory
ConnectorInfoManagerFactory cInfoManagerFactory =
    ConnectorInfoManagerFactory.getInstance();

16.2.2 The ConnectorInfoManager Interface

The ConnectorInfoManager interface maintains a list of ConnectorInfo instances. Each instance describes an identity connector. ConnectorInfoManager can be obtained by calling the getLocalManager method on the ConnectorInfoManagerFactory, and a list of bundle URLs is passed to it. ConnectorInfoManager can also by obtained by calling getRemoteManager method on the ConnectorInfoManagerFactory. The getRemoteManager method accepts an instance of RemoteFrameworkConnectionInfoand, which is used for getting information about connectors deployed on Connector Server.

In Example 16-2, cInfoManagerFactory is the instance of the ConnectorInfoManagerFactory and bundleURL is a list of bundle URLs that may point to directories consisting of JAR-ed or un-JAR-ed bundles.

Example 16-2 ConnectorInfoManager Implementation

//get the ConnectorInfoManager
ConnectorInfoManager cInfoManager =
    cInfoManagerFactory.getLocalManager(bundleURL);

16.2.3 The ConnectorKey Class

A ConnectorKey uniquely identifies a Connector instance within an installation. The ConnectorKey class takes a bundleName (name of the Connector bundle), a bundleVersion (version of the Connector bundle) and a connectorName (name of the Connector bundle) as illustrated in Example 16-3.

Example 16-3 ConnectorKey Implementation

//get the ConnectorKey reference
ConnectorKey flatFileConnectorKey =
    new ConnectorKey(bundleName, bundleVersion, connectorName);

16.2.4 The ConnectorInfo Interface

The ConnectorInfo interface contains information about a specific identity connector. It contains the display name, key and message details regarding the particular identity connector. Example 16-4 illustrates how to implement the ConnectorInfo.

Example 16-4 ConnectorInfo Implementation

//get the ConnectorInfo
ConnectorInfo info =
    cInfoManager.findConnectorInfo(flatFileConnectorKey);

In the example, cInfoManager is the ConnectorInfoManager and flatFileConnectorKey is the identity connector key.

16.2.5 The APIConfiguration Interface

The APIConfiguration interface shows the configuration properties from both the SPI and the API sides. The getConfigurationProperties method returns a ConfigurationProperties instance based on the connector Configuration implementation, initialized to the defaults. Caller can then modify the properties, as required. Example 16-5 illustrates this.

Example 16-5 APIConfiguration Definition

APIConfiguration apiConfig =
    info.createDefaultAPIConfiguration();

16.2.6 The ConfigurationProperties Interface

The ConfigurationProperties interface encapsulates the SPI Configuration and uses reflection to identify the individual properties that are available for an application to manipulate. Set all of the identity connector's configuration properties using the setPropertyValue method as defined in Example 16-6.

Example 16-6 setPropertyValue Method Signature

public void setPropertyValue
  (java.lang.String name, java.lang.Object value)

Example 16-7 illustrates an implementation of the ConfigurationProperties inteface.

Example 16-7 ConfigurationProperties Implementation

//get the default APIConfiguration
ConfigurationProperties flatFileConfigProps =
    apiConfig.getConfigurationProperties();

16.2.7 The ConnectorFacadeFactory Class

The ConnectorFacadeFactory class allows an application to get a Connector instance and to manage a pool of Connector instances. Example 16-8 illustrates the ConnectorFacadeFactory definition.

Example 16-8 ConnectorFacadeFactory Definition

//get a reference to ConnectorFacadeFactory
ConnectorFacadeFactory facadeFactory =
    ConnectorFacadeFactory.getinstance();

16.2.8 The ConnectorFacade Interface

The ConnectorFacade interface is used by the target system to invoke identity connector operations by representing a specific identity connector on the API side. Example 16-9 illustrates the ConnectorFacade implementation.

Example 16-9 ConnectorFacade Implementation

//create a ConnectorFacade (nothing but a reference to Connector on SPI side)
ConnectorFacade connectorFacade = facadeFactory.newInstance(apiConfig)

16.3 Introducing the ICF SPI

Developers implement the ICF SPI to create identity connectors. The ICF SPI is made up of many interfaces but the developer need only implement those supported by the target system. SPI can again be classified into required, operation, and feature-based interfaces. Required interfaces must be implemented irrespective of the operations supported and they help to create the connector and maintain the connection with the target system, while operation interfaces help the connector to support various operations. Feature-based interfaces support certain features supported by the ICF.

The following sections have more information.

16.3.1 Implementing the Required Interfaces

All identity connectors are required to provide an implementation of two interfaces. These two interfaces declare and initialize the identity connector with the target system. The following sections have more information.

16.3.1.1 org.identityconnectors.framework.spi.Connector

This is the main interface to declare an identity connector. Many connectors create the connection to the target system when the connection is required, removing the connection when finished with it, and disposing of any resources it has used. The interface provides the init and dispose life cycle methods for this purpose.

Note:

Connector implementations must be annotated with type org.identityconnectors.framework.spi.ConnectorClass by providing the configurationClass and displayNameKey information. The displayNameKey must be a key defined in the Messages.properties file.

Every connector implementation must be annotated with @ConnectorClass. This is required because the ICF would scan all top level .class files in the connector bundle looking for classes that have the @ConnectorClass annotation, therefore, autodiscovering connectors that are defined in the bundle. This annotation requires the following elements:

  • configurationClass: This is the configuration class for this connector. This class has all the information about the target that can be used by the connector to connect and perform various provisioning and reconciliation operations. See section "org.identityconnectors.framework.spi.Configuration" for more information on how to implement the configuration class.

  • displayNameKey: Display name key that must be present in the message catalog.

Example 16-10 is a sample connector implementation.

Example 16-10 Flat File Connector Implementation

/**
 * Flat file connector implementation. This connector supports create, 
 * delete, search and update operations.
 */
@ConnectorClass
  (configurationClass=FlatFileConfigurationImpl.class,
   displayNameKey="FLAT_FILE_CONNECTOR")
public class FlatFileConnector implements Connector,
   CreateOp, DeleteOp,SearchOp<Map<String, String>>,UpdateOp{

In Example 16-10:

  • CreateOp: Helps the connector to create an entity on the target system

  • DeleteOp: Helps the connector to delete an entity on the target system

  • SearchOp: Helps the connector to search an entity on the target system

  • UpdateOp: Helps the connector to update an existing entity on the target system

See "Implementing the Operation Interfaces" for more information.

The following sections contain information and sample code that illustrates how you might implement the Connector methods. For complete code regarding a Connector implementation, see "Developing a Flat File Connector".

16.3.1.1.1 Implementing the init Method

The init method initializes the connector. The connector initializes itself with the configuration instance as provided with the annotation @ConnectorClass. The init method takes a Configuration object as an argument. The Configuration object has all the information required by the Connector to connect to the target system.

Example 16-11 illustrates how to implement the init method of interfaces in JDK 1.6.

Note:

In this document, all code samples use the methods implementing interfaces in JDK 1.6.

Example 16-11 init Method Implementation

@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();
     log.ok("Initialization done");
 }

Note:

FlatFileIOFactory, FlatFileMetadata, FlatFileParser and FlatFileWriter are supporting classes and are not part of the ICF. An implementation of these classes is illustrated in "Developing a Flat File Connector".

The init method implementation shown in Example 16-11 does the following:

  • Stores the configuration information of the target system. This can be used later while performing an operation.

  • Initializes all the supporting classes it uses while performing any provisioning and reconciliation operations.

16.3.1.1.2 Implementing the dispose Method

The dispose method disposes of any resources held by this Connector instance. Once the method is called, the Connector instance is discarded and can not be used. Example 16-12 illustrates how to implement the dispose method.

Example 16-12 dispose Method Implementation

/**
 * Disposes any resource used by the connector.
 */
 @Override
 public void dispose() {
//close any open FileReader or FileWriter instances.

//close connection with the target

//close connection if any with database
 }
16.3.1.1.3 Implementing the getConfiguration Method

The getConfiguration method returns the Configuration instance passed to the Connector when the init method was used. Example 16-13 illustrates how to implement the getConfiguration method.

Example 16-13 getConfiguration Method Implementation

/**
 * returns the Configuration of this connector
 */
@Override
public Configuration getConfiguration() {                
    return this.flatFileConfig;
}

Note:

Sometimes, components must be able to access the Configuration instance after initialization. This is supported by the accessor method getConfiguration().

16.3.1.2 org.identityconnectors.framework.spi.Configuration

The implementation of this interface encapsulates the configuration of a connector. Configuration implementation includes all the necessary information of the target system, which is used by the Connector implementation to connect to the target system and perform various reconciliation and provisioning operations. The implementation should have a default Constructor with setters and getters defined for its properties. Every property declared may not be required but if a property is required, then it should be marked required using the annotation org.identityconnectors.framework.spi.ConfigurationProperty. Configuration implementation is a Java bean and all the instance variables (mandatory or not) do have default values. For example, a string userName is used to connect to the target system and this is a mandatory attribute. This has a default value of null. When userName is a mandatory attribute, ICF expects a value to be provided by Oracle Identity Manager. In other words, Oracle Identity Manager cannot miss out this parameter. If missed, then the connector throws ConfigurationException.

The implementation should check that all required properties are available and validated before passing itself to the Connector. The interface provides a validate method for this purpose. For example, there are three mandatory configuration parameters, such as the IP address of the target, the username to connect to the target, and the password for the user. The validate method implementation can check for non-NULL values and valid IP address by using regex.

Note:

ICF also provides a convenient base class org.identityconnectors.framework.spi.AbstractConfiguration for configuration objects to extend.

Example 16-14 Configuration Implementation

/**
 * Configuration implementation for the flat file connector. 
 */
public class FlatFileConfigurationImpl extends AbstractConfiguration{

The following sections contain information and sample code that illustrates how you might implement the Configuration methods.

The Configuration implementation must provide implementation for the following methods:

16.3.1.2.1 The validate() Method

The validate method checks that the values of all required properties are set. It also validates that all values of configuration properties are valid. In other words, it validates that all values of the configuration properties are in the expected range and have the expected format. If the configuration is not valid, then the implementations generate the most specific RuntimeException available. When no specific exception is available, the implementations can throw ConfigurationException. Example 16-15 illustrates how to implement the validate method.

Example 16-15 validate Method Implementation

@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");
        FlatFileIOFactory.getInstance(this);
    }

Here, if the target flat file provided is valid or not is checked, such as is a file, is writeable, is readable. If not valid, then an exception is generated.

Implementations of the validate method should NOT connect to the target system to validate the properties.

Note:

This implementation depends on an instance variable (private File storeFile) and a supporting class (FlatFileIOFactory). A complete implementation is illustrated in "Developing a Flat File Connector".

16.3.1.2.2 The setConnectorMessages() Method

The setConnectorMessages method sets the org.identityconnectors.framework.common.objects.ConnectorMessages message catalog instance, allowing the Connector to localize messages. Example 16-16 illustrates the setConnectorMessages method definition.

Example 16-16 setConnectorMessages Method Definition

public final void setConnectorMessages(ConnectorMessages messages) {_connectorMessages = messages;}
16.3.1.2.3 The getConnectorMessages() Method

The getConnectorMessages method returns the ConnectorMessages set by the setConnectorMessages method. Example 16-17 illustrates the getConnectorMessages method definition.

Example 16-17 getConnectorMessages Method Definition

public final ConnectorMessages getConnectorMessages() {return _connectorMessages;}

16.3.2 Implementing the Feature-based Interfaces

The following sections contain information on the interfaces used to enable identity connector pooling and attribute normalizing.

16.3.2.1 org.identityconnectors.framework.spi.PoolableConnector

Connection pooling by ICF is a feature provided by the ICF in which the framework maintains a pool of connector instances and uses them while performing provisioning and reconciliation operations. Connectors can make use of pooling by implementing the PoolableConnector interface instead of plain Connector interface. To make use of this feature, implement the PoolableConnector interface. If you implement the Connector interface, then ICF creates a new connector instance for every operation, creates a new connection with the target, completes the provisioning/reconciliation operation, removes the connection with the target system, and finally disposes this connector instance. Therefore, the advantages of implementing PoolableConnector is that a pool of configurable connector instances are maintained and are reused for many operations.

Some of configurable options are:

  • Maximum connector objects in the pool that are idle and active (_maxObjects)

  • Maximum connector objects that are idle (_maxIdle)

  • Max time to wait if the pool is waiting for a free object to become available before failing (_maxWait)

  • Minimum time to wait before evicting an idle object (_minEvictableIdleTimeMillis)

  • Minimum number of idle objects (_minIdle)

These values must be set by connector API developer, and if not provided, then the following default values are used:

  • _maxObjects = 10

  • _maxIdle = 10

  • _maxWait = 150 * 1000 ms

  • _minEvictableIdleTimeMillis = 120 * 1000 ms

  • _minIdle = 1

The PoolableConnector interface extends the Connector interface. It is implemented to enable identity connector pooling that ICF provides. ICF must make sure that the Connector instance is alive before being used. For this purpose, the interface provides a checkAlive method. Example 16-18 is a sample flat file PoolableConnector implementation.

Example 16-18 Flat File Poolable Connector Implementation

/**
 * Flat file connector implementation. This is a poolable connector
   which supports create, delete, search and update operations.
 */
@ConnectorClass
  (configurationClass=FlatFileConfigurationImpl.class,
   displayNameKey="FLAT_FILE_CONNECTOR")
public class FlatFileConnector implements PoolableConnector,
   CreateOp, DeleteOp,SearchOp<Map<String, String>>,UpdateOp{

To implement the PoolableConnector interface, provide an implementation of the checkAlive method along with all the methods discussed in Section 16.3.1.1, "org.identityconnectors.framework.spi.Connector." The checkAlive method determines if the Connector instance is alive and can be used for operations on the target system. checkAlive can be called often thus the developer should make sure the implementation is fast. The method should throw a specific RuntimeException (if available) when the Connector is no longer alive. Example 16-19 illustrates how to implement the checkAlive method.

Example 16-19 checkAlive Method Implementation

/**
* Checks if this connector is alive, if not throws a RuntimeException
*/
@Override
public void checkAlive() {
//check if the connector is still connected to target
}

16.3.2.2 org.identityconnectors.framework.spi.AttributeNormalizer

This interface must be implemented by a Connector that needs to normalize any attributes passed to it. A normalizer converts values to a standard form for the purpose of display, consumption, or comparison. For example, a normalizer might convert text values to a specific case, trim whitespace, or order the elements of a DN in a specific way.

The interface defines a normalizeAttribute method for this purpose. This method takes an ObjectClass and an Attribute to be normalized as arguments and returns the normalized Attribute. Attribute normalization is applied during many operations including:

  • Filters that are passed to SearchOp

  • Results returned from SearchOp

  • Results returned from SyncOp

  • Attributes passed to UpdateAttributeValuesOp

  • Uids returned from UpdateAttributeValuesOp

  • Attributes passed to UpdateOp

  • Uids returned from UpdateOp

  • Attributes passed to CreateOp

  • Uids returned from CreateOp

  • Uids passed to DeleteOp

Example 16-20 illustrates the normalizeAttribute method definition.

Example 16-20 normalizeAttribute Method Defintion

public Attribute normalizeAttribute (ObjectClass oClass, Attribute attribute) {
if (attribute instanceof Uid) {
return new Uid(LdapUtil.createUniformUid((String)newValues.get(0), configuration.getSuffix()));
}
}

16.3.3 Implementing the Operation Interfaces

Each operation interface defines an action that the Connector may perform on a target system, if supported by it. The operation interfaces belong to the org.identityconnectors.framework.spi.operations package. The names of these operation interfaces are listed below, but subsequent sections elaborate on each interface:

  • AuthenticateOp

  • CreateOp

  • DeleteOp

  • ResolveUsernameOp

  • SchemaOp

  • ScriptOnConnectorOp

  • ScriptOnResourceOp

  • SearchOp<T>SyncOp

  • TestOp

  • UpdateAttributeValuesOp

  • UpdateOp

The following sections contain more information on some of these operations.

16.3.3.1 Implementing the SchemaOp Interface

The SchemaOp interface is implemented to allow the connector to describe the objects it can handle on the target system. The schema that a connector returns describes the object-classes that it exposes for management. Each object-class has a name, a description, and a set of attribute definitions. Each attribute definition has a name, a syntax, and certain flags that describe its properties, such as multi-valued, single-valued, readable, or writeable.

The schema that a connector returns describes the attributes of each type of object that the connector exposes. Sometimes, this requires translation from an internal representation to this Schema format. In other instances, the Schema presents as an attribute; something that is natively available only via calls to the target API. Irrespective of how the SPI implementation accomplishes the mapping between the native representation and the corresponding ConnectorObject, the Schema provides the metadata that describes what a client can expect to find in a ConnectorObject of each type, which is objectClass.

To implement this interface, provide an implementation for the schema method as defined in Example 16-21.

Example 16-21 schema Method Signature

public Schema schema

The implementation should return the schema containing the types of objects that this identity connector supports.

Example 16-22 schema Method Implementation

@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);
      return flatFileSchemaBldr.build();
  }

Note:

The Uid should not appear in the returned schema.

16.3.3.2 Implementing the CreateOp Interface

The CreateOp interface is implemented to enable creating objects on the target system. To implement this interface, provide an implementation of the create() method, as shown in Example 16-23.

Example 16-23 create Method Signature

public Uid create
  (ObjectClass objectClass, Set<Attribute> attributes, 
   OperationOptions options)

This method takes an ObjectClass (for example, account or group), a set object attributes, and operation options. The implementation creates an object on the target system by using passed object attributes and object type defined by ObjectClass. The ObjectClass argument specifies the class of object to create. The class of object to be created is one of the inputs to the create operation. ObjectClass is the first argument to the create() method, as shown in Example 16-24.

Example 16-24 create Method Implementation

@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);
        }
     }

If the operation is successful, Uid instance representing object identifier on the target system is supposed to be created and returned. The caller can then use the Uid to refer to the created object.

16.3.3.3 Implementing the DeleteOp Interface

The DeleteOp interface is implemented to enable deleting objects from the target system. To implement this interface, provide an implementation for the delete method as defined in Example 16-25.

Example 16-25 delete Method Signature

public void delete
   (ObjectClass objectClass, Uid uid, OperationOptions options)

This method takes an ObjectClass (for example, account or group), the Uid of the object being deleted from the target system, and operation options. The implementation deletes the object identified by the provided Uid from the target system. if the object does not exist on the target system, then an org.identityconnectors.framework.common.exceptions.UnknownUidException is generated. Example 16-26 illustrates how to implement the delete method.

Example 16-26 delete Method Implementation

@Override
    public void delete(ObjectClass arg0, Uid arg1, OperationOptions arg2) {
        final String uidVal = arg1.getUidValue();
        this.flatFileWriter.deleteAccount(uidVal);
        log.ok("Account {0} deleted", uidVal);
    }

Note:

If the delete operation fails, then ICF generates subclasses of RuntimeException. See Oracle Fusion Middleware Java API Reference for Identity Connector Framework for details.

16.3.3.4 Implementing the SearchOp Interface

The SearchOp interface is implemented to enable searching objects on the target system. Here, the search operation consists of:

  • Creation of a native filter to implement search conditions that are specified generically.

  • Executing the actual query.

Implementing these methods in the SPI allows the API to support search. The API performs (by post-processing the result) any filtering that the connector does not perform, for example, by translating any specified filter conditions into native search conditions.

To implement this interface, provide an implementation for the createFilterTranslator and executeQuery methods as documented in the following sections.

16.3.3.4.1 Implementing the createFilterTranslator Method

The createFilterTranslator method returns an instance of implementation of FilterTranslator, which converts the ICF Filter object passed to it from the API side into a native query. Following the conversion, ICF passes the query to the executeQuery method. Example 16-27 illustrates the createFilterTranslator method definition.

Example 16-27 createFilterTranslator Method Signature

public FilterTranslator createFilterTranslator
   (ObjectClass oClass, OperationsOptions options)

Note:

The return value should not be null.

Example 16-28 illustrates an implementation of the createFilterTranslator method.

Example 16-28 createFilterTranslator Method Implementation

@Override
public FilterTranslator<Map<String, String>> createFilterTranslator
  (ObjectClass arg0, OperationOptions arg1) {
   return new ContainsAllValuesImpl() {
 };
}

This example supports only a single type of search predicate, which is ContainsAllValues. See "Implementation of AbstractFilterTranslator<T>" for an example of an implementation of ContainsAllValuesImpl. The implementation of ContainsAllValues translates into native form a condition of the form: Attribute A contains all of the values V(1), V(2) ... V(N).

For information on the org.identityconnectors.framework.common.objects.filter.FilterTranslator, see "Common Classes".

16.3.3.4.2 Implementing the executeQuery Method

The executeQuery method is called for every query produced by the FilterTranslator implementation (as documented in "Implementing the createFilterTranslator Method"). It takes an ObjectClass (for example, account or group), the query, an instance of ResultsHandler used as a callback to handle found objects, and operation options, as illustrated in Example 16-29.

Example 16-29 executeQuery Method Signature

public void executeQuery
   (ObjectClass oClass, T query, 
    ResultsHandler handler, OperationOptions options)

The implementation of the executeQuery method searches for the target objects by using the passed query, creates instances of ConnectorObject for each target object found, and uses ResultsHandler to handle ConnectorObjects. ConnectorObject is ICF representation of target resource object. It contains information such as ObjectClass, Uid, Name, and Set of Attributes. ConnectorObject is central to search. executeQuery streams ConnectorObjects into the ResultsHandler, and therefore, to the client. Example 16-30 illustrates how to implement the exectueQuery method.

Example 16-30 executeQuery Method Implementation

@Override
    public void executeQuery(ObjectClass objectClass,
            Map<String, String> matchSet, ResultsHandler resultHandler,
            OperationOptions ops) {
 
   
// searches the flat file for accounts which fulfil the condition 'matchSet' created by FilterTranslator
     Iterator<FlatFileUserAccount> userAccountIterator = this.flatFileParser
              .getAccountIterator(matchSet);
 
boolean handleMore = true;
     while (userAccountIterator.hasNext() && handleMore) {
          FlatFileUserAccount userAcc = userAccountIterator.next();
          ConnectorObject userAccObject = convertToConnectorObject(userAcc);
          // Let the client handle the result by doing callback
     handleMore = resultHandler.handle(userAccObject);
     }
     while (userAccountIterator.hasNext()) {
          FlatFileUserAccount userAcc = userAccountIterator.next();
          ConnectorObject userAccObject = convertToConnectorObject(userAcc);
            if (!resultHandler.handle(userAccObject)) {
                System.out.println("Not able to handle " + userAcc);
                break;
            }
        }
    }

16.3.3.5 Implementing the UpdateOp Interface

The UpdateOp interface is implemented to enable updating objects on the target system. To implement this interface, provide an implementation of the update method as defined in Example 16-31.

Example 16-31 update Method Signature

public Uid update(ObjectClass oClass, Uid uid, 
   Set<Attribute> attributes, OperationOptions options)

This method takes an ObjectClass (for example, account or group), Uid of the object being updated, a set of object attributes being updated, and operation options. The implementation updates the object on the target system identified by the Uid with the new values of attributes. If the object identified by the Uid does not exist on the target system, then an UnknowUidException is generated. Example 16-32 illustrates how to implement the update method.

Example 16-32 update Method Implementation

@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);
        log.ok("Account {0} updated", accountIdentifier);
 
   // Return new uid
        String newAccountIdentifier = accountToBeUpdated
              .getAttributeValue
                 (this.flatFileConfig.getUniqueAttributeName());
        return new Uid(newAccountIdentifier);
    }

16.3.4 Common Classes

There are many ICF classes mentioned in the previous sections. The most important classes are:

  • org.identityconnectors.framework.common.objects.Attribute

    An Attribute is a named collection of values within a target system object. A target system object may have many attributes and each may have many values. In its simplest form, an Attribute can be considered a name-value pair of a target system object. Empty and null values are supported. The developer should use org.identityconnectors.framework.common.objects.AttributeBuilder to construct Attribute instances.

    Note:

    All attributes are syntactically multivalued in this model. A particular attribute being singlevalued is only a semantic restriction.

  • org.identityconnectors.framework.common.objects.Uid

    A single-valued Attribute (Uid is a subclass of Attribute) that represents the unique identifier of an object on the target resource. Ideally, it should be immutable.

    Note:

    A singlevalued attribute is particularly relevant to UID being a unique identifier.

  • org.identityconnectors.framework.common.objects.ObjectClass

    An ObjectClass defines the type of the object on the target system. Account, group, or organization are examples of such types. ICF defines predefined ObjectClasses for account (ObjectClass.ACCOUNT) and group (ObjectClass.GROUP).

  • org.identityconnectors.framework.common.objects.ConnectorObject

    A ConnectorObject represents an object (for example, an account or group) on the target system. The developer must use org.identityconnectors.framework.common.objects.ConnectorObjectBuilder to construct a ConnectorObject.

  • org.identityconnectors.common.security.GuardedString

    A guarded string is a secure String implementation which solves the problem of storing passwords in memory in a plain String format. Passwords are stored as Bytes in an encrypted format. The encryption key will be randomly generated.

  • org.identityconnectors.framework.common.objects.filter.FilterTranslator

    A FilterTranslater object is responsible for converting all the filters specified on the API side of the ICF into native queries during a search operation. ICF Filters support both search predicates and logical operators:

    • Search predicates match objects based on the values of a specified attribute. For example, an EqualsFilter returns true when at least one value of an attribute is equal to a specified value.

    • Logical operators AND and OR join search predicates to build complex expressions. For example, an expression of the form "A AND B" is true only if both A and B are true. An expression of the form "A OR B" is true if at least one of A or B is true.

    The ICF provides the AbstractFilterTranslator<T> base class to make search implementation easier. A FilterTranslator sub class should override the following whenever possible.

    • createAndExpression(T, T)

    • createOrExpression(T, T)

    • createContainsExpression(ContainsFilter, boolean)

    • createEndsWithExpression(EndsWithFilter, boolean)

    • createEqualsExpression(EqualsFilter, boolean)

    • createGreaterThanExpression(GreaterThanFilter, boolean)

    • createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter, boolean)

    • createStartsWithExpression(StartsWithFilter, boolean)

    • createContainsAllValuesExpression(ContainsAllValuesFilter, boolean)

    For more information see Section 16.3.3.4, "Implementing the SearchOp Interface."

  • org.identityconnectors.framework.common.objects.ResultsHandler

    This is a callback interface for operations returning one or more results. The sub class should provide an implementation to the handle method whereas the caller can decide what to do with the results. Currently, this is used only by the SearchOp interface. For more information, see Section 16.3.3.4, "Implementing the SearchOp Interface."

16.4 Extending an Identity Connector Bundle

An identity connector bundle is the specific implementation for a particular target system. The bundle is a Java archive (JAR) that contains all the files required by the identity connector to connect to the target system and perform operations. It also has special attributes (defined in the MANIFEST file) that are recognized by the ICF. These are:

You extend an identity connector bundle, for example, to reuse common code. The AbtractDatabaseConnector is a good example, because different types of connectors can reuse the same basic logic that accesses database tables using JDBC. A connector for database tables might share this common code with a connector for Oracle Database users, a connector for IBM DB2 database users, and a connector for MySQL users.

A given Connector can be extended by adding the extended bundle to the /lib directory of a new bundle and creating a new class that subclasses the target class. This can be illustrated with the AbstractDatabaseConnector bundle. The common logic would be in a common bundle as follows:

Note:

You do not extend the original bundle. Instead, you extend the connector by embedding the original bundle in a new bundle that wraps the original bundle.

There would be as many database (resource) specific bundles as needed. For example:

16.5 Using an Identity Connector Server

An identity connector server is required when an identity connector bundle is not directly executed within your application. By using one or more identity connector servers, the ICF architecture permits your application to communicate with externally deployed identity connector bundles. Identity connector servers are available for Java™ and Microsoft .NET Framework applications.

A single connector server can support multiple ICF connectors, and these ICF connectors may be of different connector types. A single ICF connector can be used to communicate with multiple targets.

Figure 16-3 shows how Oracle Identity Manager connectors integrate with resources via ICF connectors:

Figure 16-3 ICF Connectors and Connector Server

Description of Figure 16-3 follows
Description of "Figure 16-3 ICF Connectors and Connector Server"

In Figure 16-3:

The types of connector servers are described in the following sections:

Tip:

Get the following information (defined during installation) for use during either Connector Server configuration.

  • Host name and IP address

  • Connector Server port

  • Connector Server key

  • SSL enabled

16.5.1 Using the Java Connector Server

A Java Connector Server is used when you do not want to execute a Java Connector Bundle in the same Java Virtual Machine (JVM) as the application. This deployment may be beneficial in terms of performance as the bundle works faster when deployed on the same host as the managed target system. In addition, use Java Connector Server to eliminate possibility of an application JVM crash because of faulty JNI-based connector.

Using the Java connector server is described in the following sections:

16.5.1.1 Installing and Configuring a Java Connector Server

To install and configure the Java Connector Server:

  1. Create a new directory on the computer on which you want to install the Java Connector Server. In this section, CONNECTOR_SERVER_HOME represents this directory.

  2. Unzip the Java Connector Server package in your new directory from Step 1. Java Connector Server is available for download in the Oracle Technology Network Web site at the following URL:

    http://www.oracle.com/technetwork/index.html

  3. In the ConnectorServer.properties file in the conf/ directory, set the properties as required by your deployment. Table 16-1 lists the properties in the ConnectorServer.properties file:

    Table 16-1 Properties in the ConnectorServer.properties File

    Property Description

    connectorserver.port

    Port on which the Java Connector Server listens for requests. The default value is 8759.

    connectorserver.bundleDir

    Directory where the connector bundles are deployed. The default value is bundles.

    connectorserver.libDir

    Directory in which to place dependent libraries. The default value is lib.

    connectorserver.usessl

    If set to true, the Java Connector Server uses SSL for secure communication. The default value is false.

    If you specify true, then use the following options on the command line when you start the Java Connector Server:

    • -Djavax.net.ssl.keyStore

    • -Djavax.net.ssl.keyStoreType (optional)

    • -Djavax.net.ssl.keyStorePassword

    connectorserver.ifaddress

    Bind address. To set this property, uncomment it in the file, if required. The bind address can be useful if there are more NICs installed on the computer.

    connectorserver.key

    Java Connector Server key.


  4. Set the properties in the ConnectorServer.properties file, as follows:

  5. The conf directory also contains the logging.properties file, which you can edit if required by your deployment.

16.5.1.2 Running the Java Connector Server on Microsoft Windows

To run the Java Connector Server on Microsoft Windows, use the ConnectorServer.bat script, as follows:

  1. Make sure that you have set the properties required by your deployment in the ConnectorServer.properties file, as described in "Installing and Configuring a Java Connector Server".

  2. Change to the CONNECTOR_SERVER_HOME\bin directory and find the ConnectorServer.bat script.

    Table 16-2 lists the options supported by the ConnectorServer.bat script:

    Table 16-2 Options Supported by the ConnectorServer.bat Script

    Option Description

    /install [serviceName] ["-J java-option"]

    Installs the Java Connector Server as a Microsoft Windows service.

    Optionally, you can specify a service name and Java options. If you do not specify a service name, then the default name is ConnectorServerJava.

    /run ["-J java-option"]

    Runs the Java Connector Server from the console.

    Optionally, you can specify Java options. For example, to run the Java Connector Server with SSL:

    ConnectorServer.bat /run
    "-J-Djavax.net.ssl.keyStore=mykeystore.jks"
    "-J-Djavax.net.ssl.keyStorePassword=password"
    

    /setKey [key]

    Sets the Java Connector Server key. The ConnectorServer.bat script stores the hashed value of the key in the connectorserver.key property in the ConnectorServer.properties file.

    /uninstall [serviceName]

    Uninstalls the Java Connector Server. If you do not specify a service name, then the script uninstalls the ConnectorServerJava service.


  3. If you need to stop the Java Connector Server, then stop the respective Microsoft Windows service.

16.5.1.3 Running the Java Connector Server on Solaris and Linux

To run the Java Connector Server on Solaris and Linux, use the connectorserver.sh script, as follows:

  1. Make sure that you have set the properties required by your deployment in the ConnectorServer.properties file, as described in "Installing and Configuring a Java Connector Server".

  2. Change to the CONNECTOR_SERVER_HOME/bin directory.

  3. Use the chmod command to set the permissions to make the connectorserver.sh script executable.

  4. Run the connectorserver.sh script.

    Table 16-3 lists the options supported by the connectorserver.sh script:

    Table 16-3 Options Supported by the connectorserver.sh Script

    Option Description

    /run [ -Jjava-option ]

    Runs the Java Connector Server in the console. Optionally, you can specify one or more Java options. For example, to run the Java Connector Server with SSL:

    ./connectorserver.sh /run
    -J-Djavax.net.ssl.keyStore=mykeystore.jks
    -J-Djavax.net.ssl.keyStorePassword=password
    

    /start [ -Jjava-option ]

    Runs the Java Connector Server in the background. Optionally, you can specify one or more Java options.

    /stop

    Stops the Java Connector Server, waiting up to 5 seconds for the process to end.

    /stop n

    Stops the Java Connector Server, waiting up to n seconds for the process to end.

    /stop -force

    Stops the Java Connector Server. Waits up to 5 seconds, and then uses the kill -KILL command if the process is still running.

    /stop n -force

    Stops the Java Connector Server. Waits up to n seconds, and then uses the kill -KILL command if the process is still running.

    /setKey key

    Sets the Java Connector Server key. The connectorserver.sh script stores the hashed value of the key in the connectorserver.key property in the ConnectorServer.properties file.


16.5.1.4 Installing an Identity Connector in a Java Connector Server

This section contains the procedures to deploy a Java Connector Bundle in a Java Connector Server.

  1. Change to the bundles directory in your Java Connector Server directory.

  2. Copy the Java Connector Bundle JAR to the bundles directory.

  3. Add any applicable third party JAR files required by the identity connector to the lib directory.

  4. Restart the Java Connector Server.

16.5.1.5 Using SSL to Communicate with a Connector Server

Follow these steps to communicate with a Connector Server using Secure Sockets Layer (SSL).

  1. Deploy an SSL certificate to the Connector Server's system.

  2. Configure your Connector Server to provide SSL sockets.

  3. Configure your application to communicate with the Connector Server using SSL.

    Refer to the target system's manual for specific notes on configuring connections to identity connector servers. You will indicate to your application that an SSL connection is required when establishing a connection for each SSL-enabled connector server. Additionally, if any of the SSL certificates used by your connector servers are issued by a non-standard certificate authority, your application must be configured to respect the additional authorities. Refer to your manual for notes regarding certificate authorities.

    Note:

    Java applications may solve the issue of non-standard certificate authorities by expecting the following Java system properties to be passed when launching the application:

    • javax.net.ssl.trustStorePassword

      For example:

      -Djavax.net.ssl.trustStorePassword=changeit

    • javax.net.ssl.trustStore

      For example:

      -Djavax.net.ssl.trustStore=/usr/myApp_cacerts

    Alternately, the non-standard certificate authorities may be imported to the standard ${JAVA_HOME}/lib/security/cacerts directory.

16.5.2 Using the Microsoft .NET Framework Connector Server

The use of a Microsoft .NET Framework (.NET) Connector Server is useful when an application is written in Java but a Connector Bundle is written using C#. Because a Java Platform, Enterprise Edition (JEE™) application cannot load C# classes, you can deploy the C# bundles under a .NET Connector Server. The Java application can then communicate with the C# (.NET) Connector Server over the network. The C# (.NET) Connector Server serves as a proxy to provide any authenticated application access to the C# bundles. The following sections contain additional information.

16.5.2.1 Installing the .NET Connector Server

The minimum requirements to run a .NET Connector Server are:

  • Microsoft Windows Server 2003 or 2008

  • Microsoft .NET Framework 3.5 or higher

Refer to the particular .NET identity connector documentation to determine if there are additional requirements.

To install the .NET Connector Server, execute the ServiceInstall.msi file and follow the instructions displayed in the Installation Wizard. Upon completion of the installation, the Connector Server will be installed as a Windows Service.

16.5.2.2 Configuring the .NET Connector Server

Follow this procedure to configure the .NET Connector Server. Common configurations include port, trace and SSL settings as well as the Connector Server key.

  1. Start the Microsoft Services Console.

  2. Check to see if the Connector Server is currently running. If yes, stop it.

  3. Set the key for the Connector Server using the command prompt.

    This key is required by any client that connects to this Connector Server.

    1. Change to the directory in which the Connector Server was installed.

      By default: \Program Files\Identity Connectors\Connector Server

    2. Execute the following command:

      ConnectorServer /setkey NEWKEY

      where NEWKEY is the value for the key.

  4. Configure additional properties by inspecting the settings in connectorserver.exe.config.

    The connectorserver.exe.config file contains information about the Connector Server. The port, SSL configuration and trace settings are most commonly changed. Port and SSL settings are in a tag called AppSettings as follows:

    <add key="connectorserver.port" value="8759" />
    <add key="connectorserver.usessl" value="false" />
    <add key="connectorserver.certificatestorename" value="ConnectorServerSSLCertificate" />
    <add key="connectorserver.ifaddress" value="0.0.0.0" />
    

    The port can be set by changing the value of connectorserver.port. To use SSL, set the value of connectorserver.usessl to true, and set the value of connectorserver.certifacatestorename to the name of your certificate store. The listening socket can be bound to a particular address, or can be left as 0.0.0.0. For more information about configuring the Connector Server with SSL, see Section 16.5.1.5, "Using SSL to Communicate with a Connector Server." For information on trace setting configurations, see Section 16.5.2.3, "Configuring Trace Settings."

16.5.2.3 Configuring Trace Settings

The Connector Server uses the standard .NET trace mechanism. Trace settings are defined in the connectorserver.exe.config configuration file. Example 16-33 illustrates how they are defined.

Example 16-33 Defined Trace Settings

<system.diagnostics>
  <trace autoflush="true" indentsize="4">
     <listeners>
       <remove name="Default" />
       <add name="myListener" 
            type="System.Diagnostics.TextWriterTraceListener" 
            initializeData="c:\connectorserver2.log"    
            traceOutputOptions="DateTime">
       <filter type="System.Diagnostics.EventTypeFilter" 
               initializeData="Information" />
       </add>
    </listeners>
  </trace>
</system.diagnostics>

The default settings are a good starting point but you may change these settings as follows.

  • For less tracing, change the filter type's initializeData setting to Warning or Error.

  • For more verbose logging, set the value to Verbose or All.

    Caution:

    The amount of logging performed has a direct effect on the performance of the Connector Servers.

Any configuration changes require that the Connector Server be stopped and restarted.

Note:

For more information about the tracing options, see Microsoft .NET documentation for System.Diagnostics.

16.5.2.4 Running the .NET Connector Server

The best way to run the .NET Connector Server is as a Windows Service. During installation, the Connector Server is installed as a Windows service. If this is not adequate for your environment, the Connector Server may be installed or uninstalled as a Windows Service by using the /install or /uninstall arguments at the command prompt.

To run the Connector Server interactively, issue the command ConnectorServer /run.

16.5.2.5 Installing Multiple Connectors on a .NET Connector Server

To install new identity connectors, change to the directory where the Connector Server was installed, extract the new identity connector ZIP into it, and restart the Connector Server.