/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.flatfile; import java.io.BufferedReader; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.identityconnectors.common.IOUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorIOException; 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.filter.AbstractFilterTranslator; import org.identityconnectors.framework.common.objects.filter.FilterTranslator; import org.identityconnectors.framework.spi.Configuration; import org.identityconnectors.framework.spi.Connector; import org.identityconnectors.framework.spi.ConnectorClass; import org.identityconnectors.framework.spi.operations.SchemaOp; import org.identityconnectors.framework.spi.operations.SearchOp; /** * Only implements search since this connector is only used to do sync. */ @ConnectorClass(configurationClass = FlatFileConfiguration.class, displayNameKey = "FlatFile") // TODO: l10n public class FlatFileConnector implements Connector, SearchOp<String>, SchemaOp { /** * Setup {@link Connector} based logging. */ private static final Log LOG = Log.getLog(FlatFileConnector.class); private static final FilterTranslator<String> FILTER_TRANSLATOR = new AbstractFilterTranslator<String>() { }; // =================================================================== // Constants // =================================================================== private final static String MSG_SKIPPING = "Skipping blank line."; // ======================================================================= // Fields // ======================================================================= /** * Configuration information passed back to the {@link Connector} by the * method {@link Connector#init(Configuration)}. */ private FlatFileConfiguration cfg; public Configuration getConfiguration() { return this.cfg; } /** * Saves the configuration for use in later calls. * * @see org.identityconnectors.framework.spi.Connector#init(org.identityconnectors.framework.spi.Configuration) */ public void init(Configuration cfg) { this.cfg = (FlatFileConfiguration) cfg; } /** * Nothing to do since there's not resources used. * * @see org.identityconnectors.framework.spi.Connector#dispose() */ public void dispose() { // in this matter do nothing.. } /** * Read the header from the file and determine the attributes for the * account object this resource supports. */ public Schema schema() { // read the header to construct an ConnectionObjectInfo.. final SchemaBuilder bld = new SchemaBuilder(getClass()); BufferedReader rdr = null; try { // open the file for reading.. rdr = this.cfg.newFileReader(); // build the connector info object.. // read the header.. Set<AttributeInfo> attrInfos = new HashSet<AttributeInfo>(); List<String> fieldNames = readHeader(rdr, this.cfg.getFieldDelimiter(), this.cfg.getTextQualifier(), this.cfg.getUniqueAttributeName()); for (String fieldName : fieldNames) { AttributeInfoBuilder abld = new AttributeInfoBuilder(); abld.setName(fieldName); abld.setCreateable(false); abld.setUpdateable(false); attrInfos.add(abld.build()); } // set it to object class account.. bld.defineObjectClass(ObjectClass.ACCOUNT_NAME, attrInfos); } catch (IOException e) { throw new IllegalStateException(e); } finally { IOUtil.quietClose(rdr); } // return the new schema object.. return bld.build(); } public FilterTranslator<String> createFilterTranslator(ObjectClass oclass, OperationOptions options) { // flat files are not queryable - don't translate anything return FILTER_TRANSLATOR; } /** * Searches for objects to return based on the filter provided with offset * and limit features. */ public void executeQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) { /** * Track the number of lines processed. */ long lines = 0; /** * Text qualifier character. */ final char textQualifier = cfg.getTextQualifier(); /** * Field delimiter. */ final char fieldSeparator = cfg.getFieldDelimiter(); /** * Unique identifier field. */ final String uniqueIdField = cfg.getUniqueAttributeName(); /** * Internal reader initialized in the constructor. */ BufferedReader rdr = null; try { rdr = cfg.newFileReader(); /** * Fields names read from the header. */ List<String> fieldNames = FlatFileConnector.readHeader(rdr, fieldSeparator, textQualifier, uniqueIdField); String line; while ((line = rdr.readLine()) != null) { ++lines; if (line.trim().length() == 0) { LOG.info(MSG_SKIPPING); continue; } LOG.ok("Processing Data Line: {0}", line); List<String> fieldValues = StringUtil.parseLine(line, fieldSeparator, textQualifier); if (fieldValues == null) { LOG.error("Error: {0}", line); break; } else { ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); for (int i = 0; i < fieldValues.size(); ++i) { String name = fieldNames.get(i); String value = fieldValues.get(i); if (name.equals(uniqueIdField)) { bld.setUid(value); bld.setName(value); } else { bld.addAttribute(name, value); } } // create the connector object.. ConnectorObject ret = bld.build(); if (!handler.handle(ret)) { break; } } } } catch (IOException e) { throw new ConnectorIOException(e); } finally { IOUtil.quietClose(rdr); } } // ======================================================================= // Helper Classes // ======================================================================= /** * Common code needed to determine the headers names for AttributeInfo and * ConnectorObjectInfo. Reads the header to determine the field names that * are supported. */ static List<String> readHeader(final BufferedReader rdr, final char fieldSeparator, final char textQualifier, final String uniqueAttribute) throws IOException { String line; List<String> ret = null; while ((line = rdr.readLine()) != null) { if (line.trim().length() == 0) { LOG.info(MSG_SKIPPING); continue; } LOG.ok("Processing Header Line: {0}", line); ret = StringUtil.parseLine(line, fieldSeparator, textQualifier); if (ret == null) { final String msg = "Error Parsing field names: Line "; throw new IllegalStateException(msg + line); } if (!ret.contains(uniqueAttribute)) { final String msg = "Error unique attribute field does not exist: Line "; throw new IllegalStateException(msg + line); } LOG.info("Found Field Names: {0}", ret); break; } return ret; } }