5 Javaを使用したアイデンティティ・コネクタの開発

Identity Connector Framework (ICF)およびOracle Identity Managerメタデータを使用して、アイデンティティ・コネクタを開発できます。

この章は、Identity Connector Framework (ICF)とOracle Identity Managerメタデータを使用してアイデンティティ・コネクタを開発するのに必要な手順全体を確認するチュートリアルとなります。ここには、重要なICFクラスとインタフェース、コネクタ・バンドルおよびコネクタ・サーバーについての情報と、フラット・ファイル・アイデンティティ・コネクタの実装のコード例や、ユーザー・プロビジョニングとリコンシリエーションのプロセス用にOracle Identity Managerメタデータを作成するコード例が含まれています。

この章の構成は、次のとおりです。

5.1 フラット・ファイル・コネクタ開発の概要

フラット・ファイル・コネクタを開発するには、構成インタフェースの実装を開発し、その後、コネクタ・クラスを実装するという手順で開発する必要があります。

開始する前に、フラット・ファイル・コネクタのすべての操作用のIO表現モジュールを準備する必要があります。このことには、次のすべてまたは一部が含まれる場合があります。

  • フラット・ファイルの列名を読み込み、メタデータ情報を準備します。

  • 指定のデリミタで区切られた、対応する列値を含むレコードをフラット・ファイルに追加します。

  • UID値に基づいて、フラット・ファイルへのレコードを削除します。

  • フラット・ファイルで検索操作を実行します。

このチュートリアルではアイデンティティ・コネクタの開発に焦点を当てているため、これらの準備の詳細は説明していません。

ノート:

次のサポート・クラスが、アイデンティティ・コネクタ操作時のファイル入力および出力の処理に使用されます。

  • org.identityconnectors.flatfile.io.FlatFileIOFactory

  • org.identityconnectors.flatfile.io.FlatFileMetadata

  • org.identityconnectors.flatfile.io.FlatFileParser

  • org.identityconnectors.flatfile.io.FlatFileWriter

入力および出力処理のサポート・クラスの実装については、ファイル入力および出力処理用のサポート・クラスを参照してください。

5.2 フラット・ファイル・コネクタの開発

フラット・ファイル・コネクタを開発するには、AbstractConfiguration、PoolableConnector、AbstractFilterTranslatorの各クラスを実装し、コネクタ・バンドルJARファイルを作成します。

この項では、フラット・ファイル・コネクタを開発する手順の概要をコード・サンプルとともに示します。次の項目が含まれます。

5.2.1 フラット・ファイル・コネクタの開発の概要

フラット・ファイル・コネクタを開発するには、AbstractConfiguration、PoolableConnector、AbstractFilterTranslatorの各クラスを実装し、コネクタ・バンドルJARファイルを作成します。

フラット・ファイル・コネクタを開発するには:

  1. org.identityconnectors.framework.spi.AbstractConfigurationベース・クラスを拡張して、フラット・ファイル・コネクタ用の構成クラスを実装します。

    構成クラスのサンプル実装については、AbstractConfigurationの実装を参照してください。

    詳細は、org.identityconnectors.framework.spi.Configurationインタフェースを参照してください。

  2. org.identityconnectors.framework.spi.Connectorインタフェースを実装して、フラット・ファイル・コネクタのコネクタ・クラスを作成します。

    PoolableConnectorクラスのサンプル実装については、PoolableConnectorの実装を参照してください。

  3. このコネクタは、ContainsAllValuesFilter操作のみをサポートします。ContainsAllValuesFilter操作を実装します。AbstractFilterTranslator<T>クラスのサンプル実装については、AbstractFilterTranslatorの実装を参照してください。

  4. コネクタ・バンドルJARを作成します。MANIFEST.MFファイルには、次のエントリが含まれている必要があります。

    • ConnectorBundle-FrameworkVersion

    • ConnectorBundle-Name

    • ConnectorBundle-Version

    MANIFEST.MFファイルの内容については、MANIFEST.MFファイルを参照してください。

  5. ステップ4で作成したコネクタ・バンドルJARを更新します。そのように行うには:

    1. コネクタ・バンドルJARを目的の場所に抽出します。

    2. JARを抽出したディレクトリ内にlibディレクトリを作成します。

    3. 依存しているサード・パーティのJARをこのlibディレクトリに追加します。

    4. ディレクトリ全体をJARします。

      ノート:

      MANIFEST.MFファイルには、ステップ4で示したエントリが含まれている必要があります。

5.2.2 AbstractConfigurationの実装

AbstractConfigurationベース・クラスは、フラット・ファイル・コネクタ用の構成クラスを実装するように拡張できます。

次に、AbstratConfigurationクラスのサンプル実装を示します。

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 PoolableConnectorの実装

org.identityconnectors.framework.spi.Connectorインタフェースは、フラット・ファイル・コネクタのコネクタ・クラスを作成するために実装されています。

次のコード・サンプルでは、CreateOp、DeleteOp、SearchOpおよびUpdateOpの各インタフェースを実装するため、4つの操作すべてをサポートします。FlatFileMetadata、FlatFileParserおよびFlatFileWriterの各クラスは、サポート・クラスです。これらの実装は、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 AbstractFilterTranslatorの実装

org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T>クラスは、フィルタ操作を定義するために実装されています。

次に、フィルタ操作を定義するorg.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T>のサンプル実装を示します。

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 MANIFEST.MFファイル

MANIFEST.MFファイルは、コネクタ・バンドルJARファイルを作成するために使用します。

MANIFEST.MFファイルの内容は次のとおりです。

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 ファイル入力および出力処理のサポート・クラス

ファイル入力および出力処理のサポート・クラスはFlatFileIOFactory、FlatFileMetaData、FlatFileParser、FlatFileWriter、FlatfileLineIterator、FlatfileUserAccount、FlatfileAccountConversionHandlerおよびMessages.Propertiesです。

この項では、ファイル入力および出力処理用の次のサポート・クラスの実装について説明します。

5.3.1 FlatFileIOFactoryサポート・クラスの実装

次のコード・サンプルは、FlatFileIOFactoryサポート・クラスの実装を示しています。

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 FlatFileMetaDataサポート・クラスの実装

次のコード・サンプルは、FlatFileMetaDataサポート・クラスの実装を示しています。

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 FlatFileParserサポート・クラスの実装

次のコード・サンプルは、FlatFileParserサポート・クラスの実装を示しています。

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 FlatFileWriterサポート・クラスの実装

次のコード・サンプルは、FlatFileWriterサポート・クラスの実装を示しています。

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 FlatfileLineIteratorサポート・クラスの実装

次のコード・サンプルは、FlatfileLineIteratorサポート・クラスの実装を示しています。

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 FlatfileUserAccountサポート・クラスの実装

次のコード・サンプルは、FlatfileUserAccountサポート・クラスの実装を示しています。

 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 FlatfileAccountConversionHandlerサポート・クラスの実装

次のコード・サンプルは、FlatfileAccountConversionHandlerサポート・クラスの実装を示しています。

 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 Messages.Propertiesサポート・クラスの実装

次のコード・サンプルは、Messages.Propertiesサポート・クラスの実装を示しています。

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 Oracle Identity Governanceデータベースへのアイデンティティ・コネクタ・バンドルのアップロード

アイデンティティ・コネクタ・バンドルは、Oracle Identity GovernanceデータベースのICFで使用できる必要があります。

ICFアイデンティティ・コネクタをOracle Identity Managerと統合するには、次に示す項に従います。これらの手順の一部には、Oracle Identity Manager Design Consoleを使用して行う構成が含まれています。

5.4.1 Oracle Identity Governanceへのコネクタ・バンドルの登録

コネクタ・バンドルは、Oracle Identity Managerに対してローカルなコネクタ・サービスで使用できる必要があります。

このことを実現する手順は、次のとおりです。

  1. Oracle Identity Managerがインストールされているマシンに、コネクタ・バンドルJARをコピーします。
  2. 次のコマンドを実行して、JARをアップロードします。

    $MW_HOME/server/bin/UploadJars.sh

    ノート:

    この章では、DW_HOMEは$MW_HOME/Oracle_IDM1を表します。

  3. ICFBundleをJARタイプとして選択します。
  4. コネクタ・バンドルJARの場所を入力します。
  5. [Enter]を押します。

5.4.2 基本的なアイデンティティ・コネクタ・メタデータの作成

コネクタ・メタデータ構成は、プロビジョニングとリコンシリエーションの両方で必要となります。

この項における次の一連の手順は、Oracle Identity Manager Design Consoleを使用して完了します。

5.4.2.1 ITリソース・タイプ定義の作成

ITリソース・タイプ定義は、リソースの接続情報の表現です。ITリソース・タイプ定義内の構成パラメータは、コネクタ・バンドルの構成パラメータと一致する必要があります。ITリソース内のパラメータの値は、バンドル構成で設定されます。

ノート:

バンドル構成によって使用されないパラメータを含めることができます。これらは、バンドル操作に悪影響は与えません。

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「リソース管理」の下のITリソース・タイプ定義をクリックします。
  3. 「サーバー・タイプ」をFlat Fileとして定義して、新しいITリソース・タイプ定義を作成します。
  4. 図5-1に示すように、次のパラメータを追加します。
    • Configuration Lookupは、リソース用のメインの構成参照のマーカーです。パラメータの名前は、Configuration Lookupである必要があります。「デフォルトのフィールド値」に値を追加することをお薦めします。

    • textFieldDelimeterは、バンドル構成のtextFieldDelimeterパラメータにマップされます。このパラメータの値が渡されます。

    • storeFileは、バンドル構成のstoreFileパラメータにマップされます。このパラメータの値が渡されます。

    図5-1 Design ConsoleのITリソース・タイプ定義

    図5-1の説明が続きます
    「図5-1 Design ConsoleのITリソース・タイプ定義」の説明
5.4.2.2 リソース・オブジェクトの作成

リソース・オブジェクトは、Oracle Identity Managerでのリソースの表現です。コネクタ・バンドルは、リソース・オブジェクトに関連付けられています。

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「リソース管理」の下の「リソース・オブジェクト」をクリックします。
  3. FLATFILEROという名前で新しいリソース・オブジェクトを作成します。

    リソース・オブジェクトはターゲット・リソースであるため、図5-2に示すように、「信頼できるソース」ボックスは選択しません。

    図5-2 Design Consoleの「リソース・オブジェクト」

    図5-2の説明が続きます
    「図5-2 Design Consoleの「リソース・オブジェクト」」の説明
5.4.2.3 参照の作成

コネクタ・バンドルでサポートされている様々なオブジェクトに対して個別の参照を定義する必要があります。この参照には、これらのオブジェクトのプロビジョニングおよびリコンシリエーションに関連する情報を含めることができます。メインの構成参照は、これらの参照へのポインタが含まれているため、オブジェクト固有の参照のルートになります。次の項では、参照の作成方法について説明します。

5.4.2.3.1 メインの構成参照の作成

(ITリソース・タイプ定義の作成で定義した)構成参照には、接続情報とはみなされないコネクタ・バンドル構成が保持されます。ITリソース・タイプ定義で構成パラメータが見つからない場合、Oracle Identity Managerは構成参照を検索します。メインの構成参照には、バンドル・プロパティおよびバンドル構成が含まれています。バンドル・プロパティ・パラメータは、正しいバンドルの識別に必要であるため、必須です。ITリソース・タイプ定義の一部として定義されていないバンドル構成(ITリソース・タイプの定義の作成を参照)は、ここで宣言できます。

ノート:

コード・キーの値は、図と完全に一致する必要があります。デコードの値は、コネクタ・バンドルに固有です。

メインの構成参照を作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「管理」の下の「参照定義」をクリックします。
  3. 新しい参照を作成し、Lookup.FF.Configurationをコードの値として追加します。
  4. 図5-3に示すように、次の参照コード情報を追加します。
    • VERSIONを必須バンドル・バージョンとして追加します。

    • org.identityconnectors.flatfileを必須バンドル名として追加します。

    • org.identityconnectors.flatfile.FlatFileConnectorを必須コネクタ名として追加します。

    • AccountIdをuniqueAttributeNameの値として追加します。AccountIdは、プロビジョニングまたはリコンサイルされるアカウントを表す一意の文字列識別子です。フラット・ファイル内の列の名前です。AccountIdは、一意であり、ユーザー(アカウントの詳細)を一意に表すために使用されます。

    • ChangeNumberをchangeLogAttributeNameの値として追加します。アカウントが作成されると、作成されたアカウントの合計数を示すために番号が付加されます。この値は、ChangeNumberと呼ばれる変数で保持されます。

    • OBJECT_TYPE_NAME Configuration Lookupは、特定のオブジェクト・タイプの構成参照です。この例では、User Configuration Lookupが定義されているため、オブジェクト・タイプはUserになります。

    図5-3 Design Consoleの「参照定義」

    図5-3の説明が続きます
    「図5-3 Design Consoleの「参照定義」」の説明
5.4.2.3.2 オブジェクト・タイプの構成参照の作成

オブジェクト・タイプの構成参照には、特定のオブジェクト・タイプに固有のパラメータが含まれています。オブジェクト・タイプは、アイデンティティ・コネクタが動作するエンティティです。ICF ObjectClassにマップされます。メインの構成参照の作成でUser Configuration Lookupが参照されているため、Userがオブジェクト・タイプとなり、この場合はObjectClass.ACCOUNTにマップされています。(RolesとUserJobDataが、その他の2つのオブジェクト・タイプです。)オブジェクト・タイプ名は、アイデンティティ・コネクタ・バンドルでサポートされているオブジェクト・クラス名と一致する必要があります。Userオブジェクト・タイプは事前定義済のObjectClass.ACCOUNTにマップされ、Groupオブジェクト・タイプは事前定義済ObjectClass.GROUPにマップされます。アイデンティティ・コネクタが複数のオブジェクトをサポートしている場合、このステップをそれぞれに対して繰り返す必要があります。

ノート:

これらのユースケースは基本機能のみを対象としているため、この構成は必須属性として保持されます。

オブジェクト・タイプの構成参照を作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「管理」の下の「参照定義」をクリックします。
  3. 新しい参照を作成し、Lookup.FF.UM.Configurationをコードとして追加します。
  4. 図5-4に示すように、次の属性を設定します。

    ノート:

    このチュートリアルは、アイデンティティ・コネクタの実行に最低限必要な構成に焦点を当てています。

    • プロビジョニング属性マップの値はLookup.FF.UM.ProvAttrMapとします。この参照には、Oracle Identity Managerフィールドとアイデンティティ・コネクタ属性との間のマッピングが含まれています。このマッピングは、プロビジョニング時に使用されます。

    • リコンシリエーション属性マップの値はLookup.FF.UM.ReconAttributeMapとします。この参照には、Oracle Identity Managerリコンシリエーション・フィールドとアイデンティティ・コネクタ属性との間のマッピングが含まれています。このマッピングは、リコンシリエーション時に使用されます。

      図5-4 Design Consoleの2番目の「参照定義」

      図5-4の説明が続きます
      「図5-4 Design Consoleの2番目の「参照定義」」の説明

5.4.3 プロビジョニング・メタデータの作成

フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するには、プロビジョニング・メタデータを作成します。そのためには、プロセス・フォーム、アダプタ、プロセス定義およびプロビジョニング属性マッピング参照を作成します。

次の項は、フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するために実行する必要があります。

5.4.3.1 プロセス・フォームの作成

プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。

この項では、プロセス・フォームとプロセス・フォームを作成する方法について説明します。次の項目が含まれます。

5.4.3.1.1 プロセス・フォームの作成

プロセス・フォームを作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「開発ツール」の下の「フォーム・デザイナ」をクリックします。
  3. 図5-5に示すように、「表名」がUD_FLAT_FILである新しいフォームを作成します。

    図5-5 Design Consoleの「フォーム・デザイナ」

    図5-5の説明が続きます
    「図5-5 Design Consoleの「フォーム・デザイナ」」の説明
  4. コネクタ・スキーマの属性に示されているように、コネクタ・スキーマで定義されている属性を追加します。
  5. 「プロパティ」タブをクリックします。
  6. 図5-6に示しているように、次のプロパティをServer(ITResourceLookupField)に追加します。
    • Required = true

    • Type = Flat File

    図5-6 Design Consoleの「フォーム・デザイナ」のプロパティ

    図5-6の説明が続きます
    「図5-6 Design Consoleの「フォーム・デザイナ」のプロパティ」の説明
  7. フォームを保存します。
  8. 「Make Version Active」をクリックします
5.4.3.1.2 プロセス・フォームについて

プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。これにより、操作のためにオブジェクト属性がコネクタ・バンドルに渡される前に、ユーザーが設定を容易に入力できます。

プロセス・フォームで定義された属性は、規則ではありません。このフォームは、アイデンティティ・コネクタに渡される必要がある属性に対するチャレンジの方法です。一般的に、アイデンティティ・コネクタのサポートされている各属性に対して属性を定義します。

ノート:

アイデンティティ・コネクタ属性で1対1のマッピングを設定することをお薦めします。

対応するITリソース・タイプ定義に関連付ける必要があるITリソースを問い合せるためのフィールドが存在する必要があります。各フィールドの変数タイプは、オブジェクト属性のタイプにマップする必要があります。

5.4.3.1.3 コネクタ・スキーマの属性

表5-1に、コネクタ・スキーマに定義されている属性をリストします。

表5-1 「フォーム・デザイナ」のフィールド

名前 バリアント フィールド・ラベル フィールド・タイプ

UD_FLAT_FIL_FIRSTNAME

文字列

TextField

UD_FLAT_FIL_UID

文字列

ユニバーサルID

TextField

UD_FLAT_FIL_CHANGENO

文字列

変更番号

TextField

UD_FLAT_FIL_MAILID

文字列

電子メールID

TextField

UD_FLAT_FIL_SERVER

long

サーバー

ITResource

UD_FLAT_FIL_LASTNAME

文字列

TextField

UD_FLAT_FIL_ACCOUNTID

文字列

アカウントID

TextField

UD_FLAT_FIL_RETURN

文字列

Return ID

TextField

ノート:

フラット・ファイルの列名は、FirstName、ChangeNo、EmailID、Server、LastNameおよびAccountIDです。

5.4.3.2 アダプタの作成

コネクタ・バンドルでサポートされているすべての操作(作成、更新、削除など)に対してアダプタを作成する必要があります。

アダプタを作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。

  2. 「開発ツール」の下の「アダプタ・ファクトリ」をクリックします。

  3. 新しいアダプタを作成し、「アダプタ名」としてFFCreateUserを追加します。

  4. 「アダプタ・タイプ」として「プロセス・タスク」を追加します。

  5. アダプタを保存します。

  6. 図5-7に示すように、変数リスト・タブをクリックして、次の変数を追加します。

    • 「タイプ」が「文字列」で、マップが実行時に解決であるobjectType。

    • 「タイプ」が「ロング」で、マップが実行時に解決であるprocessInstanceKey。

    • 「タイプ」が「文字列」で、マップが実行時に解決であるitResourceFieldName。

    図5-7 Design Consoleの「アダプタ・ファクトリ」の変数リスト

    図5-7の説明が続きます
    「図5-7 Design Consoleの「アダプタ・ファクトリ」の変数リスト」の説明
  7. 図5-8に示すように、次のサブ手順を実行して、Java機能タスクをアダプタに追加します。

    1. アダプタ・タスク・タブをクリックします。

    2. アダプタを選択し、「追加」をクリックします。

    3. タスク・オプションからJavaを選択します。

    4. APIソースから「icf-oim-intg.jar」を選択します。

    5. APIソースとして「oracle.iam.connetors.icfcommon.prov.ICProvisioninManager」を選択します。

    6. タスクのメソッドとして「createObject」を選択します。

    7. 構成を保存します。

    8. (すでに変数リストに追加した)変数を、適切なメソッド入力および出力にマップします。

    9. 構成パラメータを、適切なメソッド入力および出力にマップします。

      データベース参照はデータベース参照(アダプタ参照)にマップし、戻り変数は戻り変数(アダプタ変数)にマップします。

    図5-8 Design Consoleの「アダプタ・ファクトリ」

    図5-8の説明が続きます
    「図5-8 Design Consoleの「アダプタ・ファクトリ」」の説明
  8. アダプタを保存してビルドします。

5.4.3.3 プロセス定義の作成

プロセス定義では、特定の操作用のコネクタ・バンドルの動作を定義します。すべての操作には、関連付けられている対応タスクがあります。

次の手順では、作成操作のプロセス定義、およびプロセス・タスクの統合を構成します。

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「プロセス管理」タブの「プロセス定義」をクリックします。
  3. 図5-9に示すように、新しいプロセス定義を作成し、それにFlat Fileという名前を付けます。

    図5-9 Design Consoleの「プロセス定義」

    図5-9の説明が続きます
    「図5-9 Design Consoleの「プロセス定義」」の説明
  4. プロセスのタイプとして「プロビジョニング」を選択します。
  5. アイデンティティ・コネクタのリソース・オブジェクト名を指定します(この例ではFLATFILERO)。
  6. プロセス・フォームの表名を指定します(この例ではUD_FLAT_FIL)。
  7. プロセス・タスクを追加してCreate Userという名前を付けます。
  8. 図5-10に示すように、「Create User」をダブルクリックして、編集します。

    図5-10 Design Consoleのタスクの編集画面

    図5-10の説明が続きます
    「図5-10 Design Consoleのタスクの編集画面」の説明
  9. 統合タブをクリックします。
  10. 図5-11に示すように、「追加」をクリックして、リストからFFCreateUserアダプタを選択します。

    このアダプタは、コンパイルされた後にのみ使用できます。

    図5-11 Design Consoleの「統合」タブ

    図5-11の説明が続きます
    「図5-11 Design Consoleの「統合」タブ」の説明
  11. 次のように変数をマップして、アイデンティティ・コネクタによって返されるレスポンス・コードを設定します。
    • Adapter Return Variable - レスポンス・コード

    • Object Type - [Literal:String] User(オブジェクト・タイプの名前)

    • Process Instance Key - [Process Data] プロセス・インスタンス

    • IT Resource Field Name - [Literal:String] UD_FLAT_FIL_SERVER (ITリソース情報が含まれたフォーム・フィールド名)

  12. 図5-12に示すように、「レスポンス」タブをクリックして、レスポンスを構成します。
    • UNKNOWNを、ステータスがR(拒否)の不明なレスポンスの受信として指定できます。

    • SUCCESSを、ステータスがC(完了)の操作の完了として指定できます。

    • ERRORを、ステータスがRのエラーの発生として指定できます。

    図5-12 Design Consoleでのレスポンスの構成

    図5-12の説明が続きます
    「図5-12 Design Consoleでのレスポンスの構成」の説明
  13. タスクのオブジェクト・ステータス・マッピング・タブをクリックします。
  14. 図5-13に示すように、オブジェクト・ステータスをステータスがCのProvisionedに更新します。

    図5-13 タスクとオブジェクトのステータス・マッピング

    図5-13の説明が続きます
    「図5-13 タスクとオブジェクトのステータス・マッピング」の説明
  15. プロセス・タスクを保存します。
5.4.3.4 プロビジョニング属性マッピング参照の作成

プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。

この項では、プロビジョニング属性マッピング参照に関する次のトピックについて説明します。

5.4.3.4.1 プロビジョニング属性マッピング参照について

プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。プロビジョニング属性マッピング参照の説明を次に示します。

  • コード・キーは、プロセス・フォームのフィールド・ラベルです。

  • デコードは、アイデンティティ・コネクタ・バンドル属性です。

  • 子フォーム属性は、入力での埋込みオブジェクトとして構成できます。

  • アイデンティティ・コネクタのプロビジョニング操作は、レスポンスでUIDを返します。これは、アイデンティティ・コネクタ・バンドル属性に対してコーディングして、フォーム・フィールドに設定できます。

5.4.3.4.2 プロビジョニング属性マッピング参照の作成

プロビジョニング属性マッピング参照を作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「管理」タブの「参照定義」をクリックします。
  3. 新しい参照を作成し、Lookup.FF.UM.ProvAttrMapという名前を付けます。

    参照の名前は、オブジェクト・タイプの構成参照から参照されます。オブジェクト・タイプの構成参照の作成を参照してください。

  4. フォーム・フィールド・ラベルをコード・キーとして追加し、アイデンティティ・コネクタ・バンドル属性をデコードとして追加します。
    • Return ID : __UID__

    • Account ID: AccountId

    • Change Number: ChangeNumber

    • First Name: FirstName

    • Last Name: LastName

    • Email ID: MailId

5.4.3.4.3 プロビジョニング属性マップで使用されるフィールド・フラグ

プロビジョニング属性マッピングの場合、次のフィールド・フラグをコード・キーに追加できます。

  • LOOKUP: これは、参照リコンシリエーション・ジョブの実行によって値が取得されるすべてのフィールドに指定する必要があります。参照リコンシリエーション・ジョブから取得された値には、ITリソース名/キーが追加されています。このフラグを指定すると、追加された値をバンドルに渡す直前にICF統合で削除するのに役立ちます。たとえば、ラベルがDatabaseで、参照リコンシリエーション・ジョブの実行により値が取得されたフィールドのコード・キーは、Database[LOOKUP]のようになります。

    ノート:

    LOOKUPフラグは、プロビジョニングとリコンシリエーション両方の属性マップに指定できます。プロビジョニングの場合、ITリソース名/ITリソース・キー接頭辞を削除する必要があります。リコンシリエーションの場合、ITリソース名/ITリソース・キー接頭辞を追加する必要があります。

  • IGNORE: これは、値が無視され、バンドルに送信されないすべてのフィールドに指定する必要があります。たとえば、ラベルがDatabaseで、値をバンドルに送信する必要がないフィールドのコード・キーは、Database[IGNORE]のようになります。

  • WRITEBACK: これは、作成操作または更新操作の直後に値をプロセス・フォームに書き戻す必要があるすべてのフィールドに指定する必要があります。このフラグを追加すると、ICF統合レイヤー・コールICF Get APIが、WRITEBACKフラグのマークがある属性の値を取得するようになります。たとえば、ラベルがDatabaseで、作成/更新の直後に値がプロセス・フォームに書き戻される必要があるフィールドのコード・キーは、Database[WRITEBACK]のようになります。これが機能するには、コネクタはGetApiOpインタフェースを実装し、ConnectorObject getObject(ObjectClass objClass、Uid uid、OperationOptionsオプション)APIに実装を提供する必要があります。このAPIは、Uidが渡されたUidと同じであるアカウントのターゲットを検索し、プロセス・フォームに書き戻されるすべての属性(およびその値)が含まれたコネクタ・オブジェクトをビルドします。

    ノート:

    コネクタがGetApiOpインタフェースを実装していない場合、WRITEBACKフラグは機能せず、エラーが生成されます。

  • DATE: これは、タイプがDateと見なされる必要があるフィールドに指定する必要があり、これがない場合、値は標準の文字列と見なされます。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようになります。

  • PROVIDEONPSWDCHANGE: これは、パスワード更新が発生した場合に、バンドル(ターゲット)に提供する必要があるすべてのフィールドに指定する必要があります。一部のターゲットは、追加の属性がすべてのパスワード変更で指定されると予測します。PROVIDEONPSWDCHANGEフラグを指定することで、パスワード変更がリクエストされた場合は必ずすべての追加フィールドまたは属性を送信するようICF統合に伝えます。たとえば、ラベルがExtra Attribute Needed for Password Changeで、パスワード更新時に値をバンドル(ターゲット)に提供する必要があるフィールドのコード・キーは、Extra Attribute Needed for Password Change[PROVIDEONPSWDCHANGE]のようになります。

5.4.4 リコンシリエーション・メタデータの作成

フラット・ファイルからレコードのリコンシリエーションを構成できます。ターゲット・リコンシリエーションを例として使用できますが、信頼できるリコンシリエーションも同様の方法で構成できます。

リストされている順序で手順を実行してください。

5.4.4.1 リコンシリエーション・スケジュール済タスクの作成

デフォルトでは、リコンシリエーションはコネクタ・バンドルで検索操作を使用します。この操作は、Oracle Identity Managerを使用して構成されたスケジュール済タスクで呼び出されます。この手順は、次のサブ手順で構成されています。

5.4.4.1.1 スケジュール済タスクの定義

スケジュール済タスクを定義するには:

  1. 次の例に示すように、スケジュール済タスク詳細が含まれたデプロイメント・マネージャXMLファイルを作成します。データベースの値を、使用しているデータベースに必ず更新してください。
    <?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. このファイルをFlat File Connector User Reconciliation.xmlとして保存します。
  3. Oracle Identity System Administrationにログインします。「システム管理」の下で、「インポート」をクリックします。
  4. Flat File Connector User Reconciliation.xmlファイルを選択し、「インポート」をクリックします。
  5. ウィザードのステップを完了します。
5.4.4.1.2 スケジュール済タスクの作成

この手順では、スケジュール済タスクの作成方法について説明します。

  1. Oracle Identity Manager拡張管理にログインします。
  2. 「システム管理」タブの「スケジューラ」をクリックします。
  3. 図5-14に示すように、スケジュール・タスクを追加し、タイプとしてFlat File Connector User Reconciliationを追加します。

    図5-14 「スケジュール済タスク」画面

    図5-14の説明が続きます
    「図5-14 「スケジュール済タスク」画面」の説明
  4. パラメータを次のように設定します。
    • 「ITリソース名」の値はFlat Fileとします。

    • リソース・オブジェクト名の値はFLATFILEROとします。

    • 「オブジェクト・タイプ」の値は「ユーザー」とします。

  5. 「適用」をクリックします。
5.4.4.2 リコンシリエーション・プロファイルの作成

リコンシリエーション・プロファイルでは、リコンシリエーション時のオブジェクト属性の構造を定義します。リコンシリエーション・プロファイルには、リコンシリエーションをサポートしているすべての属性が含まれている必要があります。

リコンシリエーション・プロファイルを作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「リソース管理」の下の「リソース・オブジェクト」をクリックします。
  3. 「FLATFILERO」リソース・オブジェクトを開きます。
  4. 図5-15に示すように、オブジェクト・リコンシリエーション・タブをクリックします。

    図5-15 Design Consoleのオブジェクト・リコンシリエーション

    図5-15の説明が続きます
    「図5-15 Design Consoleのオブジェクト・リコンシリエーション」の説明
  5. 次のリコンシリエーション・フィールドを追加します。
    • First Name [String]

    • Universal ID [String]

    • Email ID [String]

    • IT Resource Name [String]

    • Last Name [String]

    • Account ID [String]、必須

  6. 構成を保存します。
5.4.4.3 リコンシリエーション・アクション・ルールの設定

リコンシリエーション・アクション・ルールでは、リコンシリエーションの動作を定義します。この手順では、一致が見つかった場合に行われるアクションを定義します。この手順では、Oracle Identity Manager Design Consoleにログインしていることを前提にしています。

  1. 「FLATFILERO」リソース・オブジェクトを開きます。
  2. 「Object Reconciliation」タブをクリックします。
  3. 図5-16に示すように、右のフレームのリコンシリエーション・アクション・ルール・タブをクリックします。

    図5-16 Design Consoleのリコンシリエーション・アクション・ルール

    図5-16の説明が続きます
    「図5-16 Design Consoleのリコンシリエーション・アクション・ルール」の説明
  4. 1つのプロセス一致が見つかった場合(「ルール条件」)およびリンクの確立(「アクション」)として定義したアクション・ルールを追加します。
  5. 1つのエンティティ一致が見つかった場合(「ルール条件」)およびリンクの確立(「アクション」)として定義したアクション・ルールを追加します。
  6. 「Create Reconciliation Profile」をクリックします
  7. 「保存」をクリックします。
5.4.4.4 リコンシリエーション・マッピングの作成

リコンシリエーション・マッピングは、プロセス定義で実行する必要があります。これは、サポートされているリコンシリエーション・フィールドを(リソース・オブジェクトから)プロセス・フォーム・フィールドにマッピングすることです。このマッピングは、ターゲット・リコンシリエーションを構成する場合にのみ必要となります。

リコンシリエーション・マッピングを作成するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「プロセス管理」の下の「プロセス定義」をクリックします。
  3. Flat Fileプロセス定義を開きます。
  4. 図5-17に示すように、リコンシリエーション・フィールド・マッピング・タブをクリックします。

    図5-17 Design Consoleのリコンシリエーション・フィールド・マッピング

    図5-17の説明が続きます
    「図5-17 Design Consoleのリコンシリエーション・フィールド・マッピング」の説明
  5. リコンシリエーション・プロファイル・フィールドとプロセス・フォーム・フィールドとの間のマッピングを追加します。
    • 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>は、Account IDをキー・フィールドとして設定します。

  6. 構成を保存します。
5.4.4.5 リコンシリエーション属性マップで使用されるフィールド・フラグ

リコンシリエーション属性マッピングの場合は、次のフィールド・フラグをコード・キーに追加できます。

  • TRUSTED: これは、アカウントのステータスを表すフィールドのリコンシリエーション属性マップで指定する必要があります。このフラグは、信頼できるリコンシリエーションにのみ指定する必要があります。これが指定されている場合、アカウントのステータスは「アクティブ」または「無効」のいずれかになります。それ以外の場合、ステータスは「有効」または「無効」のいずれかになります。たとえば、ラベルがStatusで、値が「アクティブ」または「無効」のいずれかである必要があるフィールドのコード・キーは、Status[TRUSTED]のようになります。

  • DATE: これは、リコンシリエーション属性マップにおいて、タイプがDateと見なされる必要があるフィールドに指定する必要があります。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようにする必要があります。

5.4.4.6 リコンシリエーション一致ルールの定義

リコンシリエーション一致ルールでは、ユーザー一致の計算用の式を定義します。

リコンシリエーション一致ルールを定義するには:

  1. Oracle Identity Manager Design Consoleにログインします。
  2. 「開発ツール」の下の「リコンシリエーション・ルール」フォームを開きます。
  3. 「ルールの追加」をクリックします。

    図5-18 リコンシリエーション一致ルールの追加

    図5-18の説明が続きます
    「図5-18 リコンシリエーション一致ルールの追加」の説明
  4. リソース・オブジェクト「FLATFILERO」を選択します。
  5. このルール要素を保存して追加します。

    ユーザー・プロファイル・データの「ユーザー・ログイン」は、「アカウントID」リソース属性と同等です。

  6. ルールを保存します。

5.5 フラット・ファイル・アカウントのプロビジョニング

フラット・ファイル・アカウントをプロビジョニングするには、ITリソース・パラメータおよびLookup.FF.Configurationパラメータを使用して、タイプがFlat FileであるITリソースを作成します。

フラット・ファイル・コネクタの準備は整っています。ユーザーは、Oracle Identity Managerにログインし、次の手順を使用してITリソース(ターゲット)を作成する必要があります。

  • タイプがFlat FileであるITリソースを作成します。

  • 必要に応じて、ITリソース・パラメータを指定します。

  • 必要に応じて、Lookup.FF.Configurationに構成パラメータを指定します。

5.6 Javaコネクタ・サーバーのインストール

  1. 次のURLのOracle Technology Networkサイトからコネクタ・サーバー・パッケージ(Connector_Server_122130_java.zip)をダウンロードします。
  2. コネクタ・サーバー・パッケージの内容を抽出し、connector_server_java-1.5.0.zipファイルを探します。
  3. Javaコネクタ・サーバーをインストールするディレクトリを作成します。これは、CONNECTOR_SERVER_HOMEです。
  4. connector_server_java-1.5.0.zipファイルの内容を、CONNECTOR_SERVER_HOMEディレクトリに抽出します。
  5. CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesファイルで、デプロイメントで必要になるプロパティを設定します。

    次の例スニペットは、Javaコネクタ・サーバーに同梱のConnectorServer.propertiesを示しています。

    .
    ##
    ## 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. CONNECTOR_SERVER_HOME/confディレクトリにはlogging.propertiesファイルもあり、デプロイメントで必要になる場合は編集できます。

    ノート:

    logging.propertiesファイルを使用すると、ロギングを有効または無効にし、ログ・ファイルのレベル情報を更新できます。デフォルトでは、ロギングは有効化されており、レベルはINFOに設定されています。

5.7 Oracle Identity Governance用にSSLを使用するJavaコネクタ・サーバーを構成

ConnectorServer.propertiesファイルにキーストア資格証明を指定して、Javaコネクタ・サーバーのSSLを構成できます。

そのように行うには:
  1. Oracle Identity ManagerとJavaコネクタ・サーバーの間でSSL通信に使用されるキーストアを作成します。そのように行うには:
    1. Javaコネクタ・サーバーがインストールされているホストで、Javaホーム・ディレクトリを見つけます。
    2. Javaホーム・ディレクトリから、次のコマンドを実行してキーストアを生成します。
      $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]

      たとえば:

      $JAVA_HOME/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. 次のkeytoolコマンドを実行して、新たに生成されたキーストアの証明書をファイルにエクスポートします。
      $JAVA_HOME/jre/bin/keytool -export {-alias ALIAS} {-file CERT_FILE} {-storetype STORETYPE} {-keystore KEYSTORE} [-storepass STOREPASS]

      たとえば:

      $JAVA_HOME/jre/bin/keytool -export -alias javaconnectorserver
         -file javaconnectorserver.cert
         -keypass WEBLOGIC_PASSWORD
         -keystore javaconnectorserver.jks
         -storepass WEBLOGIC_PASSWORD
    4. Oracle Identity ManagerのホストでJavaコネクタ・サーバー・キーストアの証明書をコピーします。次のコマンドを実行して、Oracle Identity Managerで使用されるトラスト・ストアに、Javaコネクタ・サーバー・キーストアのこの証明書をインポートします。
      $JAVA_HOME/jre/bin/keytool -import {-alias ALIAS} {-file CERT_FILE} [-keypass KEYPASS] {-noprompt} {-trustcacerts} {-storetype STORETYPE} {-keystore KEYSTORE} [-storepass STOREPASS]

      Oracle Identity Managerでカスタム・アイデンティティおよびカスタム信頼を使用している場合は、カスタム信頼キーストアとJava標準信頼に次の証明書をインポートします。

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

      Oracle Identity Managerでカスタム・アイデンティティとJava標準信頼を使用している場合は、この証明書をJava標準信頼にインポートします。

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

      Oracle Identity Managerでデモ・アイデンティティとデモ信頼を使用している場合は、Oracle Identity ManagerのDOMAIN_HOME/config/fmwconfig/default keystore.jksファイルおよびJava標準信頼に次の証明書をインポートします。

      $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. $CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesファイルで、このJavaコネクタ・サーバー・キーストアの場所を指定します。
    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. $CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesファイルで、このJavaコネクタ・サーバー・キーストアのパスワードを指定します。次のいずれかを実行できます。
    • ConnectorServer.propertiesconnectorserver.promptKeyStorePassword=falseを設定し、パスワードを設定します。

      cd $CONNECTOR_SERVER/bin

      UNIXの場合: connectorserver.sh /setKeyStorePassword thepassword

      Windowsの場合: ConnectorServer.bat /setKeyStorePassword thepassword

      このコマンドにより、ConnectorServer.propertiesの暗号化された値が「connectorserver.keyStorePassword」に設定されます。

      または

    • ConnectorServer.propertiesファイルでconnectorserver.promptKeyStorePassword=trueを設定することで、コネクタ・サーバーを起動するたびにキーストアのパスワードを入力するよう求めます。

  4. $CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesファイルでconnectorserver.protocolプロパティを設定することで、セキュアな通信のためのプロトコルを設定できます。
    ## Protocol in use for SSL communication e.g. TLSv1, TLSv1.1, TLSv1.2
    ##
    connectorserver.protocol=TLSv1

    このプロパティのデフォルト値は、TLS1.0プロトコルを表すTLSv1です。

    ノート:

    Javaコネクタ・サーバーとターゲット・システムの間でSSLを構成できます。そのように行うには:

    • Javaコネクタ・サーバー・マシンでJAVA_HOMEフォルダ・パスを確認してください。

    • 次のコマンドを使用して、Javaコネクタ・サーバーのJava標準信頼キーストア(<JAVA_HOME>/jre/lib/security/cacerts)にターゲット・システムの証明書をインポートします。

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

      ここで、oidstoreは別名、JAVA_HOMEはJavaコネクタ・サーバー・マシンのJavaホーム・フォルダ、/scratch/cert/b64certificate.txtはターゲット・システムの証明書ファイル、PASSWORDはパスワードです。

5.8 Oracle Identity Governance用にSSLを使用しないJavaコネクタ・サーバーを構成

SSLを使用しないJavaコネクタ・サーバーを構成するには:
  1. $CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesファイルで、/setKeyオプションを指定してJavaコネクタ・サーバーを実行して、connectorserver.keyプロパティを設定します。

    WindowsでのJavaコネクタ・サーバーの場合は、$CONNECTOR_SERVER_HOME\binディレクトリに移動し、ConnectorServer.batスクリプトを探します。次のスクリプトを実行します。

    ./ ConnectorServer.bat /setKey <KEY>

    SolarisおよびLinuxでのJavaコネクタ・サーバーの場合、$CONNECTOR_SERVER_HOME\binディレクトリに移動し、ConnectorServer.shスクリプトを見つけます。次のスクリプトを実行します。

    ./ ConnectorServer.sh /setKey <KEY>
  2. その他すべてのプロパティについては、ConnectorServer.propertiesファイルを手動で編集します。

    Javaコネクタ・サーバーに付属するConnectorServer.propertiesのサンプル・スニペットについては、.NETコネクタ・サーバーのインストールを参照してください。

5.9 Javaコネクタ・サーバーのアップグレード

Javaコネクタ・サーバー・パックの12.2.1.3.0バージョンでは、connectorserver.protocolプロパティを使用してOracle Identity ManagerとJavaコネクタ・サーバーの間のSSL通信のプロトコルを選択できます。このプロパティのサポートされている値は、TLSv1TLSv1.1TLSv1.2です。ここで、TLSv1はTLS1.0プロトコル、TLSv1.1はTLS1.1プロトコル、TLSv1.2はTLS1.2プロトコルをそれぞれ示しています。このプロパティのデフォルト値はTLSv1で、TLS 1.0プロトコルを意味します。

Javaコネクタ・サーバーをアップグレードするには:

  1. コネクタ・サーバー・サービスを停止します。
  2. コネクタ・サーバーがインストールされているディレクトリのバックアップを作成します。
  3. 次のURLのOracle Technology Networkサイトからコネクタ・サーバー・パッケージ(Connector_Server_122130_java.zip)をダウンロードします。
  4. コネクタ・サーバー・パッケージ(Connector_Server_122130_java.zip)の内容を抽出し、connector_server_java-1.5.0.zipファイルを探します。
  5. connector_server_java-1.5.0.zipファイルの内容をディレクトリに抽出します。
  6. 12.2.1.3.0 Javaコネクタ・サーバー・パックからJavaコネクタ・サーバーのインストール場所に、connector_server_java-1.5.0/bin/およびconnector_server_java-1.5.0/lib/のディレクトリにあるファイルをコピーします。
  7. 12.2.1.3.0 Javaコネクタ・サーバー・パックからconnector_server_java-1.5.0/conf/ConnectorServer.propertiesファイルを開き、Javaコネクタ・サーバーのインストール場所からconf/ConnectorServer.propertiesファイルを開きます。
  8. 12.2.1.3.0 Javaコネクタ・サーバー・パックでconnector_server_java-1.5.0/conf/ConnectorServer.propertiesファイルから、インストールされた場所にあるconf/ConnectorServer.propertiesファイルに次のセクションを追加します。
    ## Protocol in use for SSL communication e.g. TLSv1, TLSv1.1, TLSv1.2
    connectorserver.protocol=TLSv1

    このプロパティには、SSL通信のプロトコルを選択するオプションを指定します。デフォルト値はTLS1.0です。

    ノート:

    コネクタ・サーバーのアップグレード中に、カスタマイズ内容は保存されます。更新されたファイルのいずれかでカスタマイズを行っている場合、同じカスタマイズを再実行するとバックアップ・ファイルが生成されます。
  9. 必要な設定をすべて実行した後でコネクタ・サーバーを起動します。