0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License, Version 1.0 only
0N/A * (the "License"). You may not use this file except in compliance
0N/A * with the License.
0N/A *
0N/A * You can obtain a copy of the license at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
0N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
0N/A * add the following below this CDDL HEADER, with the fields enclosed
873N/A * by brackets "[]" replaced with your own identifying information:
0N/A * Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A *
0N/A *
5137N/A * Copyright 2006-2010 Sun Microsystems, Inc.
6318N/A * Portions Copyright 2012-2013 ForgeRock AS
0N/A */
0N/Apackage org.opends.server.util;
6318N/A
6318N/Aimport java.io.*;
0N/Aimport java.net.URL;
5849N/Aimport java.util.*;
4591N/Aimport java.util.concurrent.atomic.AtomicLong;
5849N/Aimport org.opends.messages.Message;
5849N/Aimport org.opends.messages.MessageBuilder;
5849N/Aimport static org.opends.messages.UtilityMessages.*;
5849N/Aimport org.opends.server.api.plugin.PluginResult;
5849N/Aimport org.opends.server.backends.jeb.EntryID;
5849N/Aimport org.opends.server.backends.jeb.RootContainer;
5849N/Aimport org.opends.server.backends.jeb.importLDIF.Importer;
5849N/Aimport org.opends.server.backends.jeb.importLDIF.Suffix;
0N/Aimport org.opends.server.core.DirectoryServer;
0N/Aimport org.opends.server.core.PluginConfigManager;
5849N/Aimport static org.opends.server.loggers.ErrorLogger.logError;
5849N/Aimport static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
5849N/Aimport static org.opends.server.loggers.debug.DebugLogger.getTracer;
5849N/Aimport org.opends.server.loggers.debug.DebugTracer;
165N/Aimport org.opends.server.protocols.ldap.LDAPAttribute;
165N/Aimport org.opends.server.protocols.ldap.LDAPModification;
4134N/Aimport org.opends.server.types.*;
5849N/Aimport static org.opends.server.util.StaticUtils.addSuperiorObjectClasses;
5849N/Aimport static org.opends.server.util.StaticUtils.toLowerCase;
5849N/Aimport static org.opends.server.util.Validator.ensureNotNull;
0N/A
0N/A
0N/A/**
0N/A * This class provides the ability to read information from an LDIF file. It
0N/A * provides support for both standard entries and change entries (as would be
0N/A * used with a tool like ldapmodify).
0N/A */
2095N/A@org.opends.server.types.PublicAPI(
2095N/A stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
2095N/A mayInstantiate=true,
2095N/A mayExtend=false,
2095N/A mayInvoke=true)
6318N/Apublic final class LDIFReader implements Closeable
0N/A{
1400N/A /**
1400N/A * The tracer object for the debug logger.
1400N/A */
1400N/A private static final DebugTracer TRACER = getTracer();
1400N/A
0N/A // The reader that will be used to read the data.
0N/A private BufferedReader reader;
0N/A
0N/A // The buffer to use to read data from a URL.
0N/A private byte[] buffer;
0N/A
0N/A // The import configuration that specifies what should be imported.
0N/A private LDIFImportConfig importConfig;
0N/A
0N/A // The lines that comprise the body of the last entry read.
0N/A private LinkedList<StringBuilder> lastEntryBodyLines;
0N/A
0N/A // The lines that comprise the header (DN and any comments) for the last entry
0N/A // read.
0N/A private LinkedList<StringBuilder> lastEntryHeaderLines;
0N/A
4591N/A
0N/A // The number of entries that have been ignored by this LDIF reader because
0N/A // they didn't match the criteria.
4591N/A private final AtomicLong entriesIgnored = new AtomicLong();
0N/A
0N/A // The number of entries that have been read by this LDIF reader, including
0N/A // those that were ignored because they didn't match the criteria, and
0N/A // including those that were rejected because they were invalid in some way.
4591N/A private final AtomicLong entriesRead = new AtomicLong();
0N/A
0N/A // The number of entries that have been rejected by this LDIF reader.
4591N/A private final AtomicLong entriesRejected = new AtomicLong();
0N/A
0N/A // The line number on which the last entry started.
0N/A private long lastEntryLineNumber;
0N/A
0N/A // The line number of the last line read from the LDIF file, starting with 1.
0N/A private long lineNumber;
0N/A
0N/A // The plugin config manager that will be used if we are to invoke plugins
0N/A // on the entries as they are read.
0N/A private PluginConfigManager pluginConfigManager;
0N/A
4591N/A private RootContainer rootContainer;
4591N/A
0N/A
0N/A /**
0N/A * Creates a new LDIF reader that will read information from the specified
0N/A * file.
0N/A *
402N/A * @param importConfig The import configuration for this LDIF reader. It
402N/A * must not be <CODE>null</CODE>.
0N/A *
0N/A * @throws IOException If a problem occurs while opening the LDIF file for
0N/A * reading.
0N/A */
0N/A public LDIFReader(LDIFImportConfig importConfig)
0N/A throws IOException
0N/A {
402N/A ensureNotNull(importConfig);
0N/A this.importConfig = importConfig;
0N/A
0N/A reader = importConfig.getReader();
0N/A buffer = new byte[4096];
0N/A lineNumber = 0;
0N/A lastEntryLineNumber = -1;
0N/A lastEntryBodyLines = new LinkedList<StringBuilder>();
0N/A lastEntryHeaderLines = new LinkedList<StringBuilder>();
0N/A pluginConfigManager = DirectoryServer.getPluginConfigManager();
4748N/A // If we should invoke import plugins, then do so.
4748N/A if (importConfig.invokeImportPlugins())
4748N/A {
4748N/A // Inform LDIF import plugins that an import session is ending
4748N/A pluginConfigManager.invokeLDIFImportBeginPlugins(importConfig);
4748N/A }
0N/A }
0N/A
0N/A
4591N/A /**
4591N/A * Creates a new LDIF reader that will read information from the
4591N/A * specified file.
4591N/A *
4591N/A * @param importConfig
4591N/A * The import configuration for this LDIF reader. It must not
4591N/A * be <CODE>null</CODE>.
4591N/A * @param rootContainer The root container needed to get the next entry ID.
4591N/A * @param size The size of the buffer to read the LDIF bytes into.
4591N/A *
4591N/A * @throws IOException
4591N/A * If a problem occurs while opening the LDIF file for
4591N/A * reading.
4591N/A */
4591N/A public LDIFReader(LDIFImportConfig importConfig, RootContainer rootContainer,
4591N/A int size)
4591N/A throws IOException
4591N/A {
4591N/A ensureNotNull(importConfig);
4591N/A this.importConfig = importConfig;
4591N/A this.reader = importConfig.getReader();
4591N/A this.lineNumber = 0;
4591N/A this.lastEntryLineNumber = -1;
4591N/A this.lastEntryBodyLines = new LinkedList<StringBuilder>();
4591N/A this.lastEntryHeaderLines = new LinkedList<StringBuilder>();
4591N/A this.pluginConfigManager = DirectoryServer.getPluginConfigManager();
4591N/A this.buffer = new byte[size];
4591N/A this.rootContainer = rootContainer;
4748N/A // If we should invoke import plugins, then do so.
4748N/A if (importConfig.invokeImportPlugins())
4748N/A {
4748N/A // Inform LDIF import plugins that an import session is ending
4748N/A this.pluginConfigManager.invokeLDIFImportBeginPlugins(importConfig);
4748N/A }
4591N/A }
4591N/A
4591N/A
4591N/A
0N/A
0N/A /**
0N/A * Reads the next entry from the LDIF source.
0N/A *
0N/A * @return The next entry read from the LDIF source, or <CODE>null</CODE> if
0N/A * the end of the LDIF data is reached.
0N/A *
0N/A * @throws IOException If an I/O problem occurs while reading from the file.
0N/A *
0N/A * @throws LDIFException If the information read cannot be parsed as an LDIF
0N/A * entry.
0N/A */
0N/A public Entry readEntry()
0N/A throws IOException, LDIFException
0N/A {
0N/A return readEntry(importConfig.validateSchema());
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4643N/A * Reads the next entry from the LDIF source.
4591N/A *
4591N/A * @return The next entry read from the LDIF source, or <CODE>null</CODE> if
4591N/A * the end of the LDIF data is reached.
4591N/A *
4643N/A * @param map A map of suffixes instances.
4643N/A *
4643N/A * @param entryInfo A object to hold information about the entry ID and what
4643N/A * suffix was selected.
4591N/A *
4591N/A * @throws IOException If an I/O problem occurs while reading from the file.
4591N/A *
4591N/A * @throws LDIFException If the information read cannot be parsed as an LDIF
4591N/A * entry.
4591N/A */
4643N/A public final Entry readEntry(Map<DN, Suffix> map,
4643N/A Importer.EntryInformation entryInfo)
4591N/A throws IOException, LDIFException
4591N/A {
4643N/A return readEntry(importConfig.validateSchema(), map, entryInfo);
4591N/A }
4591N/A
4591N/A
4591N/A
4765N/A private Entry readEntry(boolean checkSchema, Map<DN, Suffix> map,
4643N/A Importer.EntryInformation entryInfo)
4591N/A throws IOException, LDIFException
4591N/A {
4591N/A while (true)
4591N/A {
4591N/A LinkedList<StringBuilder> lines;
4591N/A DN entryDN;
4765N/A EntryID entryID;
4765N/A Suffix suffix;
4591N/A synchronized (this)
4591N/A {
4591N/A // Read the set of lines that make up the next entry.
4591N/A lines = readEntryLines();
4591N/A if (lines == null)
4591N/A {
4591N/A return null;
4591N/A }
4591N/A lastEntryBodyLines = lines;
4591N/A lastEntryHeaderLines = new LinkedList<StringBuilder>();
4591N/A
4591N/A
4591N/A // Read the DN of the entry and see if it is one that should be included
4591N/A // in the import.
4765N/A
4765N/A try
4765N/A {
4765N/A entryDN = readDN(lines);
4765N/A } catch (LDIFException le) {
4765N/A continue;
4765N/A }
4591N/A if (entryDN == null)
4591N/A {
4591N/A // This should only happen if the LDIF starts with the "version:" line
4591N/A // and has a blank line immediately after that. In that case, simply
4591N/A // read and return the next entry.
4591N/A continue;
4591N/A }
4591N/A else if (!importConfig.includeEntry(entryDN))
4591N/A {
4591N/A if (debugEnabled())
4591N/A {
4591N/A TRACER.debugInfo("Skipping entry %s because the DN isn't" +
4591N/A "one that should be included based on the include and " +
4591N/A "exclude branches.", entryDN);
4591N/A }
4591N/A entriesRead.incrementAndGet();
4591N/A Message message = ERR_LDIF_SKIP.get(String.valueOf(entryDN));
4591N/A logToSkipWriter(lines, message);
4591N/A continue;
4591N/A }
4591N/A entryID = rootContainer.getNextEntryID();
4707N/A suffix = Importer.getMatchSuffix(entryDN, map);
4707N/A if(suffix == null)
4591N/A {
4707N/A if (debugEnabled())
4707N/A {
4707N/A TRACER.debugInfo("Skipping entry %s because the DN isn't" +
4707N/A "one that should be included based on a suffix match" +
4707N/A "check." ,entryDN);
4707N/A }
4707N/A entriesRead.incrementAndGet();
4707N/A Message message = ERR_LDIF_SKIP.get(String.valueOf(entryDN));
4707N/A logToSkipWriter(lines, message);
4707N/A continue;
4591N/A }
4591N/A entriesRead.incrementAndGet();
4707N/A suffix.addPending(entryDN);
4591N/A }
4591N/A // Read the set of attributes from the entry.
4591N/A HashMap<ObjectClass,String> objectClasses =
5849N/A new HashMap<ObjectClass,String>();
5849N/A HashMap<AttributeType,List<AttributeBuilder>> userAttrBuilders =
5849N/A new HashMap<AttributeType,List<AttributeBuilder>>();
5849N/A HashMap<AttributeType,List<AttributeBuilder>> operationalAttrBuilders =
5849N/A new HashMap<AttributeType,List<AttributeBuilder>>();
4591N/A try
4591N/A {
4591N/A for (StringBuilder line : lines)
4591N/A {
5849N/A readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders,
5849N/A operationalAttrBuilders, checkSchema);
4591N/A }
4591N/A }
4591N/A catch (LDIFException e)
4591N/A {
5849N/A if (debugEnabled())
5849N/A {
6318N/A TRACER.debugInfo("Skipping entry %s because reading" +
5849N/A "its attributes failed.", entryDN);
5849N/A }
5849N/A Message message = ERR_LDIF_READ_ATTR_SKIP.get(String.valueOf(entryDN),
4660N/A e.getMessage());
6318N/A logToSkipWriter(lines, message);
5849N/A suffix.removePending(entryDN);
5849N/A continue;
4591N/A }
4591N/A
4591N/A // Create the entry and see if it is one that should be included in the
4591N/A // import.
5849N/A HashMap<AttributeType,List<Attribute>> userAttributes =
5849N/A new HashMap<AttributeType,List<Attribute>>(
5849N/A userAttrBuilders.size());
5849N/A HashMap<AttributeType,List<Attribute>> operationalAttributes =
5849N/A new HashMap<AttributeType,List<Attribute>>(
5849N/A operationalAttrBuilders.size());
5849N/A for (Map.Entry<AttributeType, List<AttributeBuilder>>
5849N/A attrTypeEntry : userAttrBuilders.entrySet())
5849N/A {
5849N/A AttributeType attrType = attrTypeEntry.getKey();
5849N/A List<AttributeBuilder> attrBuilderList = attrTypeEntry.getValue();
5849N/A List<Attribute> attrList =
5849N/A new ArrayList<Attribute>(attrBuilderList.size());
5849N/A for (AttributeBuilder builder : attrBuilderList)
5849N/A {
5849N/A attrList.add(builder.toAttribute());
5849N/A }
5849N/A userAttributes.put(attrType, attrList);
5849N/A }
5849N/A for (Map.Entry<AttributeType, List<AttributeBuilder>>
5849N/A attrTypeEntry : operationalAttrBuilders.entrySet())
5849N/A {
5849N/A AttributeType attrType = attrTypeEntry.getKey();
5849N/A List<AttributeBuilder> attrBuilderList = attrTypeEntry.getValue();
5849N/A List<Attribute> attrList =
5849N/A new ArrayList<Attribute>(attrBuilderList.size());
5849N/A for (AttributeBuilder builder : attrBuilderList)
5849N/A {
5849N/A attrList.add(builder.toAttribute());
5849N/A }
5849N/A operationalAttributes.put(attrType, attrList);
5849N/A }
5849N/A // Create the entry and see if it is one that should be included in the
5849N/A // import.
4591N/A Entry entry = new Entry(entryDN, objectClasses, userAttributes,
4591N/A operationalAttributes);
4591N/A TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, entry.toString());
4591N/A
4591N/A try
4591N/A {
4591N/A if (! importConfig.includeEntry(entry))
4591N/A {
4591N/A if (debugEnabled())
4591N/A {
4591N/A TRACER.debugInfo("Skipping entry %s because the DN is not one " +
4591N/A "that should be included based on the include and exclude " +
4591N/A "filters.", entryDN);
4591N/A }
4591N/A Message message = ERR_LDIF_SKIP.get(String.valueOf(entryDN));
4591N/A logToSkipWriter(lines, message);
4591N/A suffix.removePending(entryDN);
4591N/A continue;
4591N/A }
4591N/A }
4591N/A catch (Exception e)
4591N/A {
4591N/A if (debugEnabled())
4591N/A {
4591N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
4591N/A }
4591N/A suffix.removePending(entryDN);
4591N/A Message message = ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.
4591N/A get(String.valueOf(entry.getDN()), lastEntryLineNumber,
4591N/A String.valueOf(e));
5852N/A logToSkipWriter(lines, message);
5852N/A suffix.removePending(entryDN);
5852N/A continue;
4591N/A }
4591N/A
4591N/A
4591N/A // If we should invoke import plugins, then do so.
4591N/A if (importConfig.invokeImportPlugins())
4591N/A {
4591N/A PluginResult.ImportLDIF pluginResult =
4591N/A pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
4591N/A if (! pluginResult.continueProcessing())
4591N/A {
4591N/A Message m;
4591N/A Message rejectMessage = pluginResult.getErrorMessage();
4591N/A if (rejectMessage == null)
4591N/A {
4591N/A m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(
4591N/A String.valueOf(entryDN));
4591N/A }
4591N/A else
4591N/A {
4591N/A m = ERR_LDIF_REJECTED_BY_PLUGIN.get(String.valueOf(entryDN),
4591N/A rejectMessage);
4591N/A }
4591N/A
4591N/A logToRejectWriter(lines, m);
4591N/A suffix.removePending(entryDN);
4591N/A continue;
4591N/A }
4591N/A }
4591N/A
4591N/A
4591N/A // Make sure that the entry is valid as per the server schema if it is
4591N/A // appropriate to do so.
4591N/A if (checkSchema)
4591N/A {
5169N/A //Add the RDN attributes.
5169N/A addRDNAttributesIfNecessary(entryDN,userAttributes,
5169N/A operationalAttributes);
5169N/A //Add any superior objectclass(s) missing in the objectclass map.
5169N/A addSuperiorObjectClasses(objectClasses);
5169N/A
4591N/A MessageBuilder invalidReason = new MessageBuilder();
4591N/A if (! entry.conformsToSchema(null, false, true, false, invalidReason))
4591N/A {
4591N/A Message message = ERR_LDIF_SCHEMA_VIOLATION.get(
4591N/A String.valueOf(entryDN),
4591N/A lastEntryLineNumber,
4591N/A invalidReason.toString());
4591N/A logToRejectWriter(lines, message);
4591N/A suffix.removePending(entryDN);
4660N/A continue;
4591N/A }
4591N/A }
4643N/A entryInfo.setEntryID(entryID);
4643N/A entryInfo.setSuffix(suffix);
4591N/A // The entry should be included in the import, so return it.
4591N/A return entry;
4591N/A }
4591N/A }
4591N/A
4591N/A
4591N/A
4591N/A /**
0N/A * Reads the next entry from the LDIF source.
0N/A *
0N/A * @param checkSchema Indicates whether this reader should perform schema
0N/A * checking on the entry before returning it to the
0N/A * caller. Note that some basic schema checking (like
0N/A * refusing multiple values for a single-valued
0N/A * attribute) may always be performed.
0N/A *
0N/A *
0N/A * @return The next entry read from the LDIF source, or <CODE>null</CODE> if
0N/A * the end of the LDIF data is reached.
0N/A *
0N/A * @throws IOException If an I/O problem occurs while reading from the file.
0N/A *
0N/A * @throws LDIFException If the information read cannot be parsed as an LDIF
0N/A * entry.
0N/A */
0N/A public Entry readEntry(boolean checkSchema)
0N/A throws IOException, LDIFException
0N/A {
0N/A while (true)
0N/A {
0N/A // Read the set of lines that make up the next entry.
0N/A LinkedList<StringBuilder> lines = readEntryLines();
0N/A if (lines == null)
0N/A {
0N/A return null;
0N/A }
0N/A lastEntryBodyLines = lines;
0N/A lastEntryHeaderLines = new LinkedList<StringBuilder>();
0N/A
0N/A
0N/A // Read the DN of the entry and see if it is one that should be included
0N/A // in the import.
0N/A DN entryDN = readDN(lines);
0N/A if (entryDN == null)
0N/A {
0N/A // This should only happen if the LDIF starts with the "version:" line
0N/A // and has a blank line immediately after that. In that case, simply
0N/A // read and return the next entry.
0N/A continue;
0N/A }
868N/A else if (!importConfig.includeEntry(entryDN))
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugInfo("Skipping entry %s because the DN is not one that " +
1400N/A "should be included based on the include and exclude branches.",
868N/A entryDN);
868N/A }
4591N/A entriesRead.incrementAndGet();
2086N/A Message message = ERR_LDIF_SKIP.get(String.valueOf(entryDN));
1773N/A logToSkipWriter(lines, message);
0N/A continue;
0N/A }
0N/A else
0N/A {
4591N/A entriesRead.incrementAndGet();
0N/A }
0N/A
0N/A // Read the set of attributes from the entry.
0N/A HashMap<ObjectClass,String> objectClasses =
0N/A new HashMap<ObjectClass,String>();
4495N/A HashMap<AttributeType,List<AttributeBuilder>> userAttrBuilders =
4495N/A new HashMap<AttributeType,List<AttributeBuilder>>();
4495N/A HashMap<AttributeType,List<AttributeBuilder>> operationalAttrBuilders =
4495N/A new HashMap<AttributeType,List<AttributeBuilder>>();
16N/A try
0N/A {
16N/A for (StringBuilder line : lines)
16N/A {
4495N/A readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders,
4495N/A operationalAttrBuilders, checkSchema);
16N/A }
0N/A }
16N/A catch (LDIFException e)
16N/A {
16N/A throw e;
16N/A }
0N/A
0N/A // Create the entry and see if it is one that should be included in the
0N/A // import.
4495N/A HashMap<AttributeType,List<Attribute>> userAttributes =
4495N/A new HashMap<AttributeType,List<Attribute>>(
4495N/A userAttrBuilders.size());
4495N/A HashMap<AttributeType,List<Attribute>> operationalAttributes =
4495N/A new HashMap<AttributeType,List<Attribute>>(
4495N/A operationalAttrBuilders.size());
4495N/A for (Map.Entry<AttributeType, List<AttributeBuilder>>
4495N/A attrTypeEntry : userAttrBuilders.entrySet())
4495N/A {
4495N/A AttributeType attrType = attrTypeEntry.getKey();
4495N/A List<AttributeBuilder> attrBuilderList = attrTypeEntry.getValue();
4495N/A List<Attribute> attrList =
4495N/A new ArrayList<Attribute>(attrBuilderList.size());
4495N/A for (AttributeBuilder builder : attrBuilderList)
4495N/A {
4495N/A attrList.add(builder.toAttribute());
4495N/A }
4495N/A userAttributes.put(attrType, attrList);
4495N/A }
4495N/A for (Map.Entry<AttributeType, List<AttributeBuilder>>
4495N/A attrTypeEntry : operationalAttrBuilders.entrySet())
4495N/A {
4495N/A AttributeType attrType = attrTypeEntry.getKey();
4495N/A List<AttributeBuilder> attrBuilderList = attrTypeEntry.getValue();
4495N/A List<Attribute> attrList =
4495N/A new ArrayList<Attribute>(attrBuilderList.size());
4495N/A for (AttributeBuilder builder : attrBuilderList)
4495N/A {
4495N/A attrList.add(builder.toAttribute());
4495N/A }
4495N/A operationalAttributes.put(attrType, attrList);
4495N/A }
0N/A Entry entry = new Entry(entryDN, objectClasses, userAttributes,
0N/A operationalAttributes);
4134N/A TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, entry.toString());
0N/A
0N/A try
0N/A {
0N/A if (! importConfig.includeEntry(entry))
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugInfo("Skipping entry %s because the DN is not one " +
1400N/A "that should be included based on the include and exclude " +
1400N/A "filters.", entryDN);
868N/A }
2086N/A Message message = ERR_LDIF_SKIP.get(String.valueOf(entryDN));
1773N/A logToSkipWriter(lines, message);
0N/A continue;
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A
2086N/A Message message = ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.
2086N/A get(String.valueOf(entry.getDN()), lastEntryLineNumber,
2086N/A String.valueOf(e));
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
0N/A }
0N/A
0N/A
0N/A // If we should invoke import plugins, then do so.
0N/A if (importConfig.invokeImportPlugins())
0N/A {
3352N/A PluginResult.ImportLDIF pluginResult =
0N/A pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
3352N/A if (! pluginResult.continueProcessing())
0N/A {
2373N/A Message m;
3352N/A Message rejectMessage = pluginResult.getErrorMessage();
2373N/A if (rejectMessage == null)
2373N/A {
2373N/A m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(
2373N/A String.valueOf(entryDN));
2373N/A }
2373N/A else
2373N/A {
2373N/A m = ERR_LDIF_REJECTED_BY_PLUGIN.get(String.valueOf(entryDN),
2373N/A rejectMessage);
2373N/A }
2373N/A
2373N/A logToRejectWriter(lines, m);
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A
0N/A // Make sure that the entry is valid as per the server schema if it is
0N/A // appropriate to do so.
0N/A if (checkSchema)
0N/A {
2086N/A MessageBuilder invalidReason = new MessageBuilder();
723N/A if (! entry.conformsToSchema(null, false, true, false, invalidReason))
0N/A {
2086N/A Message message = ERR_LDIF_SCHEMA_VIOLATION.get(
2086N/A String.valueOf(entryDN),
2086N/A lastEntryLineNumber,
2086N/A invalidReason.toString());
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
0N/A }
4388N/A //Add any superior objectclass(s) missing in an entries
4388N/A //objectclass map.
4388N/A addSuperiorObjectClasses(entry.getObjectClasses());
0N/A }
0N/A
0N/A
0N/A // The entry should be included in the import, so return it.
0N/A return entry;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Reads the next change record from the LDIF source.
0N/A *
0N/A * @param defaultAdd Indicates whether the change type should default to
0N/A * "add" if none is explicitly provided.
0N/A *
0N/A * @return The next change record from the LDIF source, or <CODE>null</CODE>
0N/A * if the end of the LDIF data is reached.
0N/A *
0N/A * @throws IOException If an I/O problem occurs while reading from the file.
0N/A *
0N/A * @throws LDIFException If the information read cannot be parsed as an LDIF
0N/A * entry.
0N/A */
0N/A public ChangeRecordEntry readChangeRecord(boolean defaultAdd)
0N/A throws IOException, LDIFException
0N/A {
0N/A while (true)
0N/A {
0N/A // Read the set of lines that make up the next entry.
0N/A LinkedList<StringBuilder> lines = readEntryLines();
0N/A if (lines == null)
0N/A {
0N/A return null;
0N/A }
0N/A
0N/A
0N/A // Read the DN of the entry and see if it is one that should be included
0N/A // in the import.
0N/A DN entryDN = readDN(lines);
0N/A if (entryDN == null)
0N/A {
0N/A // This should only happen if the LDIF starts with the "version:" line
0N/A // and has a blank line immediately after that. In that case, simply
0N/A // read and return the next entry.
0N/A continue;
0N/A }
0N/A
0N/A String changeType = readChangeType(lines);
0N/A
4591N/A ChangeRecordEntry entry;
0N/A
0N/A if(changeType != null)
0N/A {
0N/A if(changeType.equals("add"))
0N/A {
165N/A entry = parseAddChangeRecordEntry(entryDN, lines);
0N/A } else if (changeType.equals("delete"))
0N/A {
165N/A entry = parseDeleteChangeRecordEntry(entryDN, lines);
0N/A } else if (changeType.equals("modify"))
0N/A {
165N/A entry = parseModifyChangeRecordEntry(entryDN, lines);
0N/A } else if (changeType.equals("modrdn"))
0N/A {
165N/A entry = parseModifyDNChangeRecordEntry(entryDN, lines);
0N/A } else if (changeType.equals("moddn"))
0N/A {
165N/A entry = parseModifyDNChangeRecordEntry(entryDN, lines);
0N/A } else
0N/A {
2086N/A Message message = ERR_LDIF_INVALID_CHANGETYPE_ATTRIBUTE.get(
2086N/A changeType, "add, delete, modify, moddn, modrdn");
2086N/A throw new LDIFException(message, lastEntryLineNumber, false);
0N/A }
0N/A } else
0N/A {
0N/A // default to "add"?
0N/A if(defaultAdd)
0N/A {
165N/A entry = parseAddChangeRecordEntry(entryDN, lines);
0N/A } else
0N/A {
2086N/A Message message = ERR_LDIF_INVALID_CHANGETYPE_ATTRIBUTE.get(
2086N/A null, "add, delete, modify, moddn, modrdn");
2086N/A throw new LDIFException(message, lastEntryLineNumber, false);
0N/A }
0N/A }
0N/A
0N/A return entry;
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Reads a set of lines from the next entry in the LDIF source.
0N/A *
0N/A * @return A set of lines from the next entry in the LDIF source.
0N/A *
0N/A * @throws IOException If a problem occurs while reading from the LDIF
0N/A * source.
0N/A *
0N/A * @throws LDIFException If the information read is not valid LDIF.
0N/A */
0N/A private LinkedList<StringBuilder> readEntryLines()
0N/A throws IOException, LDIFException
0N/A {
0N/A // Read the entry lines into a buffer.
0N/A LinkedList<StringBuilder> lines = new LinkedList<StringBuilder>();
0N/A int lastLine = -1;
0N/A
4591N/A if(reader == null)
4591N/A {
4591N/A return null;
4591N/A }
4591N/A
0N/A while (true)
0N/A {
0N/A String line = reader.readLine();
0N/A lineNumber++;
0N/A
0N/A if (line == null)
0N/A {
0N/A // This must mean that we have reached the end of the LDIF source.
0N/A // If the set of lines read so far is empty, then move onto the next
0N/A // file or return null. Otherwise, break out of this loop.
0N/A if (lines.isEmpty())
0N/A {
0N/A reader = importConfig.nextReader();
0N/A if (reader == null)
0N/A {
0N/A return null;
0N/A }
0N/A else
0N/A {
0N/A return readEntryLines();
0N/A }
0N/A }
0N/A else
0N/A {
0N/A break;
0N/A }
0N/A }
0N/A else if (line.length() == 0)
0N/A {
0N/A // This is a blank line. If the set of lines read so far is empty,
0N/A // then just skip over it. Otherwise, break out of this loop.
0N/A if (lines.isEmpty())
0N/A {
0N/A continue;
0N/A }
0N/A else
0N/A {
0N/A break;
0N/A }
0N/A }
0N/A else if (line.charAt(0) == '#')
0N/A {
0N/A // This is a comment. Ignore it.
0N/A continue;
0N/A }
465N/A else if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t'))
0N/A {
0N/A // This is a continuation of the previous line. If there is no
465N/A // previous line, then that's a problem. Note that while RFC 2849
465N/A // technically only allows a space in this position, both OpenLDAP and
465N/A // the Sun Java System Directory Server allow a tab as well, so we will
465N/A // too for compatibility reasons. See issue #852 for details.
0N/A if (lastLine >= 0)
0N/A {
0N/A lines.get(lastLine).append(line.substring(1));
0N/A }
0N/A else
0N/A {
2086N/A Message message =
2086N/A ERR_LDIF_INVALID_LEADING_SPACE.get(lineNumber, line);
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lineNumber, false);
0N/A }
0N/A }
0N/A else
0N/A {
0N/A // This is a new line.
0N/A if (lines.isEmpty())
0N/A {
0N/A lastEntryLineNumber = lineNumber;
0N/A }
5137N/A if(((byte)line.charAt(0) == (byte)0xEF) &&
5137N/A ((byte)line.charAt(1) == (byte)0xBB) &&
5137N/A ((byte)line.charAt(2) == (byte)0xBF))
5137N/A {
5137N/A // This is a UTF-8 BOM that Java doesn't skip. We will skip it here.
5137N/A line = line.substring(3, line.length());
5137N/A }
0N/A lines.add(new StringBuilder(line));
0N/A lastLine++;
0N/A }
0N/A }
0N/A
0N/A
0N/A return lines;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Reads the DN of the entry from the provided list of lines. The DN must be
0N/A * the first line in the list, unless the first line starts with "version",
0N/A * in which case the DN should be the second line.
0N/A *
0N/A * @param lines The set of lines from which the DN should be read.
0N/A *
0N/A * @return The decoded entry DN.
0N/A *
0N/A * @throws LDIFException If DN is not the first element in the list (or the
0N/A * second after the LDIF version), or if a problem
0N/A * occurs while trying to parse it.
0N/A */
0N/A private DN readDN(LinkedList<StringBuilder> lines)
0N/A throws LDIFException
0N/A {
0N/A if (lines.isEmpty())
0N/A {
0N/A // This is possible if the contents of the first "entry" were just
0N/A // the version identifier. If that is the case, then return null and
0N/A // use that as a signal to the caller to go ahead and read the next entry.
0N/A return null;
0N/A }
0N/A
0N/A StringBuilder line = lines.remove();
0N/A lastEntryHeaderLines.add(line);
0N/A int colonPos = line.indexOf(":");
0N/A if (colonPos <= 0)
0N/A {
2086N/A Message message =
2086N/A ERR_LDIF_NO_ATTR_NAME.get(lastEntryLineNumber, line.toString());
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
0N/A }
0N/A
0N/A String attrName = toLowerCase(line.substring(0, colonPos));
0N/A if (attrName.equals("version"))
0N/A {
0N/A // This is the version line, and we can skip it.
0N/A return readDN(lines);
0N/A }
0N/A else if (! attrName.equals("dn"))
0N/A {
2086N/A Message message =
2086N/A ERR_LDIF_NO_DN.get(lastEntryLineNumber, line.toString());
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
0N/A }
0N/A
0N/A
0N/A // Look at the character immediately after the colon. If there is none,
0N/A // then assume the null DN. If it is another colon, then the DN must be
0N/A // base64-encoded. Otherwise, it may be one or more spaces.
0N/A int length = line.length();
0N/A if (colonPos == (length-1))
0N/A {
509N/A return DN.nullDN();
0N/A }
0N/A
0N/A if (line.charAt(colonPos+1) == ':')
0N/A {
0N/A // The DN is base64-encoded. Find the first non-blank character and
0N/A // take the rest of the line, base64-decode it, and parse it as a DN.
0N/A int pos = colonPos+2;
0N/A while ((pos < length) && (line.charAt(pos) == ' '))
0N/A {
0N/A pos++;
0N/A }
0N/A
0N/A String encodedDNStr = line.substring(pos);
0N/A
0N/A String dnStr;
0N/A try
0N/A {
0N/A dnStr = new String(Base64.decode(encodedDNStr), "UTF-8");
0N/A }
0N/A catch (Exception e)
0N/A {
0N/A // The value did not have a valid base64-encoding.
868N/A if (debugEnabled())
868N/A {
4765N/A TRACER.debugInfo("Base64 decode failed for dn: ",
4765N/A line.substring(pos));
868N/A }
0N/A
2086N/A Message message =
2086N/A ERR_LDIF_COULD_NOT_BASE64_DECODE_DN.get(
2086N/A lastEntryLineNumber, line,
2086N/A String.valueOf(e));
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
0N/A }
0N/A
0N/A try
0N/A {
0N/A return DN.decode(dnStr);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
4765N/A TRACER.debugInfo("DN decode failed for: ", dnStr);
868N/A }
0N/A
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lastEntryLineNumber, line.toString(),
2086N/A de.getMessageObject());
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, de);
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
4765N/A TRACER.debugInfo("DN decode failed for: ", dnStr);
868N/A }
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lastEntryLineNumber, line.toString(),
2086N/A String.valueOf(e));
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
0N/A }
0N/A }
0N/A else
0N/A {
0N/A // The rest of the value should be the DN. Skip over any spaces and
0N/A // attempt to decode the rest of the line as the DN.
0N/A int pos = colonPos+1;
0N/A while ((pos < length) && (line.charAt(pos) == ' '))
0N/A {
0N/A pos++;
0N/A }
0N/A
0N/A String dnString = line.substring(pos);
0N/A
0N/A try
0N/A {
0N/A return DN.decode(dnString);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
4765N/A TRACER.debugInfo("DN decode failed for: ", line.substring(pos));
868N/A }
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lastEntryLineNumber, line.toString(), de.getMessageObject());
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, de);
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
4765N/A TRACER.debugInfo("DN decode failed for: ", line.substring(pos));
868N/A }
0N/A
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lastEntryLineNumber, line.toString(),
2086N/A String.valueOf(e));
0N/A
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
165N/A
0N/A /**
0N/A * Reads the changetype of the entry from the provided list of lines. If
0N/A * there is no changetype attribute then an add is assumed.
0N/A *
0N/A * @param lines The set of lines from which the DN should be read.
0N/A *
0N/A * @return The decoded entry DN.
0N/A *
0N/A * @throws LDIFException If DN is not the first element in the list (or the
0N/A * second after the LDIF version), or if a problem
0N/A * occurs while trying to parse it.
0N/A */
0N/A private String readChangeType(LinkedList<StringBuilder> lines)
0N/A throws LDIFException
0N/A {
0N/A if (lines.isEmpty())
0N/A {
0N/A // Error. There must be other entries.
0N/A return null;
0N/A }
0N/A
0N/A StringBuilder line = lines.get(0);
0N/A lastEntryHeaderLines.add(line);
0N/A int colonPos = line.indexOf(":");
0N/A if (colonPos <= 0)
0N/A {
2086N/A Message message = ERR_LDIF_NO_ATTR_NAME.get(
2086N/A lastEntryLineNumber, line.toString());
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
0N/A }
0N/A
0N/A String attrName = toLowerCase(line.substring(0, colonPos));
0N/A if (! attrName.equals("changetype"))
0N/A {
0N/A // No changetype attribute - return null
0N/A return null;
0N/A } else
0N/A {
0N/A // Remove the line
165N/A lines.remove();
0N/A }
0N/A
0N/A
0N/A // Look at the character immediately after the colon. If there is none,
0N/A // then no value was specified. Throw an exception
0N/A int length = line.length();
0N/A if (colonPos == (length-1))
0N/A {
2086N/A Message message = ERR_LDIF_INVALID_CHANGETYPE_ATTRIBUTE.get(
2086N/A null, "add, delete, modify, moddn, modrdn");
2086N/A throw new LDIFException(message, lastEntryLineNumber, false );
0N/A }
0N/A
0N/A if (line.charAt(colonPos+1) == ':')
0N/A {
0N/A // The change type is base64-encoded. Find the first non-blank
0N/A // character and
0N/A // take the rest of the line, and base64-decode it.
0N/A int pos = colonPos+2;
0N/A while ((pos < length) && (line.charAt(pos) == ' '))
0N/A {
0N/A pos++;
0N/A }
0N/A
0N/A String encodedChangeTypeStr = line.substring(pos);
0N/A
0N/A String changeTypeStr;
0N/A try
0N/A {
0N/A changeTypeStr = new String(Base64.decode(encodedChangeTypeStr),
0N/A "UTF-8");
0N/A }
0N/A catch (Exception e)
0N/A {
0N/A // The value did not have a valid base64-encoding.
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A
2086N/A Message message = ERR_LDIF_COULD_NOT_BASE64_DECODE_DN.get(
2086N/A lastEntryLineNumber, line,
2086N/A String.valueOf(e));
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
0N/A }
0N/A
0N/A return changeTypeStr;
0N/A }
0N/A else
0N/A {
0N/A // The rest of the value should be the changetype.
0N/A // Skip over any spaces and
0N/A // attempt to decode the rest of the line as the changetype string.
0N/A int pos = colonPos+1;
0N/A while ((pos < length) && (line.charAt(pos) == ' '))
0N/A {
0N/A pos++;
0N/A }
0N/A
4765N/A return line.substring(pos);
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Decodes the provided line as an LDIF attribute and adds it to the
0N/A * appropriate hash.
0N/A *
0N/A * @param lines The full set of lines that comprise the
0N/A * entry (used for writing reject information).
0N/A * @param line The line to decode.
0N/A * @param entryDN The DN of the entry being decoded.
0N/A * @param objectClasses The set of objectclasses decoded so far for
0N/A * the current entry.
4591N/A * @param userAttrBuilders The map of user attribute builders decoded
4591N/A * so far for the current entry.
4591N/A * @param operationalAttrBuilders The map of operational attribute builders
4591N/A * decoded so far for the current entry.
1101N/A * @param checkSchema Indicates whether to perform schema
1101N/A * validation for the attribute.
0N/A *
0N/A * @throws LDIFException If a problem occurs while trying to decode the
0N/A * attribute contained in the provided entry.
0N/A */
0N/A private void readAttribute(LinkedList<StringBuilder> lines,
0N/A StringBuilder line, DN entryDN,
0N/A HashMap<ObjectClass,String> objectClasses,
4495N/A HashMap<AttributeType,List<AttributeBuilder>> userAttrBuilders,
4495N/A HashMap<AttributeType,List<AttributeBuilder>> operationalAttrBuilders,
1101N/A boolean checkSchema)
0N/A throws LDIFException
0N/A {
165N/A // Parse the attribute type description.
165N/A int colonPos = parseColonPosition(lines, line);
165N/A String attrDescr = line.substring(0, colonPos);
3853N/A final Attribute attribute = parseAttrDescription(attrDescr);
3853N/A final String attrName = attribute.getName();
3853N/A final String lowerName = toLowerCase(attrName);
0N/A
165N/A // Now parse the attribute value.
4134N/A ByteString value = parseSingleValue(lines, line, entryDN,
165N/A colonPos, attrName);
0N/A
0N/A // See if this is an objectclass or an attribute. Then get the
0N/A // corresponding definition and add the value to the appropriate hash.
0N/A if (lowerName.equals("objectclass"))
0N/A {
0N/A if (! importConfig.includeObjectClasses())
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugVerbose("Skipping objectclass %s for entry %s due to " +
1400N/A "the import configuration.", value, entryDN);
868N/A }
0N/A return;
0N/A }
0N/A
4314N/A String ocName = value.toString().trim();
0N/A String lowerOCName = toLowerCase(ocName);
0N/A
0N/A ObjectClass objectClass = DirectoryServer.getObjectClass(lowerOCName);
0N/A if (objectClass == null)
0N/A {
0N/A objectClass = DirectoryServer.getDefaultObjectClass(ocName);
0N/A }
0N/A
0N/A if (objectClasses.containsKey(objectClass))
0N/A {
2086N/A logError(WARN_LDIF_DUPLICATE_OBJECTCLASS.get(
2086N/A String.valueOf(entryDN), lastEntryLineNumber, ocName));
0N/A }
0N/A else
0N/A {
0N/A objectClasses.put(objectClass, ocName);
0N/A }
0N/A }
0N/A else
0N/A {
0N/A AttributeType attrType = DirectoryServer.getAttributeType(lowerName);
0N/A if (attrType == null)
0N/A {
0N/A attrType = DirectoryServer.getDefaultAttributeType(attrName);
0N/A }
0N/A
0N/A
0N/A if (! importConfig.includeAttribute(attrType))
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugVerbose("Skipping attribute %s for entry %s due to the " +
1400N/A "import configuration.", attrName, entryDN);
868N/A }
0N/A return;
0N/A }
0N/A
3853N/A //The attribute is not being ignored so check for binary option.
4032N/A if(checkSchema && !attrType.isBinary())
3853N/A {
3853N/A if(attribute.hasOption("binary"))
3853N/A {
3853N/A Message message = ERR_LDIF_INVALID_ATTR_OPTION.get(
3853N/A String.valueOf(entryDN),lastEntryLineNumber, attrName);
3853N/A logToRejectWriter(lines, message);
3853N/A throw new LDIFException(message, lastEntryLineNumber,true);
3853N/A }
3853N/A }
1764N/A if (checkSchema &&
1764N/A (DirectoryServer.getSyntaxEnforcementPolicy() !=
1764N/A AcceptRejectWarn.ACCEPT))
1764N/A {
2086N/A MessageBuilder invalidReason = new MessageBuilder();
1764N/A if (! attrType.getSyntax().valueIsAcceptable(value, invalidReason))
1764N/A {
2086N/A Message message = WARN_LDIF_VALUE_VIOLATES_SYNTAX.get(
2086N/A String.valueOf(entryDN),
4134N/A lastEntryLineNumber, value.toString(),
2086N/A attrName, invalidReason.toString());
1764N/A if (DirectoryServer.getSyntaxEnforcementPolicy() ==
1764N/A AcceptRejectWarn.WARN)
1764N/A {
2086N/A logError(message);
1764N/A }
1764N/A else
1764N/A {
1764N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber,
1764N/A true);
1764N/A }
1764N/A }
1764N/A }
0N/A
4134N/A AttributeValue attributeValue =
4134N/A AttributeValues.create(attrType, value);
4495N/A List<AttributeBuilder> attrList;
0N/A if (attrType.isOperational())
0N/A {
4495N/A attrList = operationalAttrBuilders.get(attrType);
0N/A if (attrList == null)
0N/A {
3853N/A AttributeBuilder builder = new AttributeBuilder(attribute, true);
3853N/A builder.add(attributeValue);
4495N/A attrList = new ArrayList<AttributeBuilder>();
4495N/A attrList.add(builder);
4495N/A operationalAttrBuilders.put(attrType, attrList);
0N/A return;
0N/A }
0N/A }
0N/A else
0N/A {
4495N/A attrList = userAttrBuilders.get(attrType);
0N/A if (attrList == null)
0N/A {
3853N/A AttributeBuilder builder = new AttributeBuilder(attribute, true);
3853N/A builder.add(attributeValue);
4495N/A attrList = new ArrayList<AttributeBuilder>();
4495N/A attrList.add(builder);
4495N/A userAttrBuilders.put(attrType, attrList);
0N/A return;
0N/A }
0N/A }
0N/A
0N/A // Check to see if any of the attributes in the list have the same set of
0N/A // options. If so, then try to add a value to that attribute.
5850N/A for (AttributeBuilder a : attrList)
5850N/A {
5850N/A if (a.optionsEqual(attribute.getOptions()))
5850N/A {
5850N/A if (!a.add(attributeValue) && checkSchema)
5850N/A {
2086N/A Message message = WARN_LDIF_DUPLICATE_ATTR.get(
2086N/A String.valueOf(entryDN),
2086N/A lastEntryLineNumber, attrName,
4134N/A value.toString());
1101N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber,
4765N/A true);
0N/A }
5850N/A if (attrType.isSingleValue() && (a.size() > 1) && checkSchema)
5850N/A {
2086N/A Message message = ERR_LDIF_MULTIPLE_VALUES_FOR_SINGLE_VALUED_ATTR
2086N/A .get(String.valueOf(entryDN),
4765N/A lastEntryLineNumber, attrName);
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
0N/A }
1101N/A
1101N/A return;
0N/A }
0N/A }
0N/A
3853N/A // No set of matching options was found, so create a new one and
3853N/A // add it to the list.
3853N/A AttributeBuilder builder = new AttributeBuilder(attribute, true);
3853N/A builder.add(attributeValue);
4495N/A attrList.add(builder);
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Decodes the provided line as an LDIF attribute and returns the
0N/A * Attribute (name and values) for the specified attribute name.
0N/A *
0N/A * @param lines The full set of lines that comprise the
0N/A * entry (used for writing reject information).
0N/A * @param line The line to decode.
0N/A * @param entryDN The DN of the entry being decoded.
770N/A * @param attributeName The name and options of the attribute to
770N/A * return the values for.
0N/A *
0N/A * @return The attribute in octet string form.
0N/A * @throws LDIFException If a problem occurs while trying to decode
0N/A * the attribute contained in the provided
0N/A * entry or if the parsed attribute name does
0N/A * not match the specified attribute name.
0N/A */
165N/A private Attribute readSingleValueAttribute(
0N/A LinkedList<StringBuilder> lines, StringBuilder line, DN entryDN,
0N/A String attributeName) throws LDIFException
0N/A {
165N/A // Parse the attribute type description.
165N/A int colonPos = parseColonPosition(lines, line);
0N/A String attrDescr = line.substring(0, colonPos);
0N/A Attribute attribute = parseAttrDescription(attrDescr);
0N/A String attrName = attribute.getName();
0N/A
770N/A if (attributeName != null)
0N/A {
770N/A Attribute expectedAttr = parseAttrDescription(attributeName);
770N/A
770N/A if (!attribute.equals(expectedAttr))
770N/A {
2086N/A Message message = ERR_LDIF_INVALID_CHANGERECORD_ATTRIBUTE.get(
2086N/A attrDescr, attributeName);
2086N/A throw new LDIFException(message, lastEntryLineNumber, false);
770N/A }
0N/A }
0N/A
165N/A // Now parse the attribute value.
4134N/A ByteString value = parseSingleValue(lines, line, entryDN,
165N/A colonPos, attrName);
0N/A
3853N/A AttributeBuilder builder = new AttributeBuilder(attribute, true);
0N/A AttributeType attrType = attribute.getAttributeType();
4134N/A builder.add(AttributeValues.create(attrType, value));
0N/A
3853N/A return builder.toAttribute();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Retrieves the starting line number for the last entry read from the LDIF
0N/A * source.
0N/A *
0N/A * @return The starting line number for the last entry read from the LDIF
0N/A * source.
0N/A */
0N/A public long getLastEntryLineNumber()
0N/A {
0N/A return lastEntryLineNumber;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Rejects the last entry read from the LDIF. This method is intended for use
0N/A * by components that perform their own validation of entries (e.g., backends
0N/A * during import processing) in which the entry appeared valid to the LDIF
0N/A * reader but some other problem was encountered.
0N/A *
0N/A * @param message A human-readable message providing the reason that the
0N/A * last entry read was not acceptable.
0N/A */
2086N/A public void rejectLastEntry(Message message)
0N/A {
4591N/A entriesRejected.incrementAndGet();
0N/A
0N/A BufferedWriter rejectWriter = importConfig.getRejectWriter();
0N/A if (rejectWriter != null)
0N/A {
0N/A try
0N/A {
402N/A if ((message != null) && (message.length() > 0))
402N/A {
402N/A rejectWriter.write("# ");
2086N/A rejectWriter.write(message.toString());
402N/A rejectWriter.newLine();
402N/A }
0N/A
0N/A for (StringBuilder sb : lastEntryHeaderLines)
0N/A {
0N/A rejectWriter.write(sb.toString());
0N/A rejectWriter.newLine();
0N/A }
0N/A
0N/A for (StringBuilder sb : lastEntryBodyLines)
0N/A {
0N/A rejectWriter.write(sb.toString());
0N/A rejectWriter.newLine();
0N/A }
0N/A
0N/A rejectWriter.newLine();
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A }
0N/A }
0N/A }
0N/A
4328N/A /**
4328N/A * Log the specified entry and messages in the reject writer. The method is
4328N/A * intended to be used in a threaded environment, where individual import
4328N/A * threads need to log an entry and message to the reject file.
4328N/A *
4328N/A * @param e The entry to log.
4328N/A * @param message The message to log.
4328N/A */
4328N/A public synchronized void rejectEntry(Entry e, Message message) {
4328N/A BufferedWriter rejectWriter = importConfig.getRejectWriter();
4591N/A entriesRejected.incrementAndGet();
4328N/A if (rejectWriter != null) {
4328N/A try {
4328N/A if ((message != null) && (message.length() > 0)) {
4328N/A rejectWriter.write("# ");
4328N/A rejectWriter.write(message.toString());
4328N/A rejectWriter.newLine();
4328N/A }
4328N/A String dnStr = e.getDN().toString();
4328N/A rejectWriter.write(dnStr);
4328N/A rejectWriter.newLine();
4328N/A List<StringBuilder> eLDIF = e.toLDIF();
4328N/A for(StringBuilder l : eLDIF) {
4328N/A rejectWriter.write(l.toString());
4328N/A rejectWriter.newLine();
4328N/A }
4328N/A rejectWriter.newLine();
4328N/A } catch (IOException ex) {
4328N/A if (debugEnabled())
4328N/A TRACER.debugCaught(DebugLogLevel.ERROR, ex);
4328N/A }
4328N/A }
4328N/A }
4328N/A
0N/A
0N/A
0N/A /**
0N/A * Closes this LDIF reader and the underlying file or input stream.
0N/A */
0N/A public void close()
0N/A {
4748N/A // If we should invoke import plugins, then do so.
4748N/A if (importConfig.invokeImportPlugins())
4748N/A {
4748N/A // Inform LDIF import plugins that an import session is ending
4748N/A pluginConfigManager.invokeLDIFImportEndPlugins(importConfig);
4748N/A }
0N/A importConfig.close();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
3853N/A * Parse an AttributeDescription (an attribute type name and its
3853N/A * options).
3853N/A *
3853N/A * @param attrDescr
3853N/A * The attribute description to be parsed.
3853N/A * @return A new attribute with no values, representing the
3853N/A * attribute type and its options.
0N/A */
4518N/A public static Attribute parseAttrDescription(String attrDescr)
0N/A {
3853N/A AttributeBuilder builder;
0N/A int semicolonPos = attrDescr.indexOf(';');
0N/A if (semicolonPos > 0)
0N/A {
3853N/A builder = new AttributeBuilder(attrDescr.substring(0, semicolonPos));
3853N/A int nextPos = attrDescr.indexOf(';', semicolonPos + 1);
0N/A while (nextPos > 0)
0N/A {
3853N/A String option = attrDescr.substring(semicolonPos + 1, nextPos);
0N/A if (option.length() > 0)
0N/A {
3853N/A builder.setOption(option);
0N/A semicolonPos = nextPos;
3853N/A nextPos = attrDescr.indexOf(';', semicolonPos + 1);
0N/A }
0N/A }
0N/A
3853N/A String option = attrDescr.substring(semicolonPos + 1);
0N/A if (option.length() > 0)
0N/A {
3853N/A builder.setOption(option);
0N/A }
0N/A }
0N/A else
0N/A {
3853N/A builder = new AttributeBuilder(attrDescr);
0N/A }
0N/A
3853N/A if(builder.getAttributeType().isBinary())
0N/A {
3853N/A //resetting doesn't hurt and returns false.
3853N/A builder.setOption("binary");
0N/A }
0N/A
3853N/A return builder.toAttribute();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the total number of entries read so far by this LDIF reader,
0N/A * including those that have been ignored or rejected.
0N/A *
0N/A * @return The total number of entries read so far by this LDIF reader.
0N/A */
0N/A public long getEntriesRead()
0N/A {
4591N/A return entriesRead.get();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the total number of entries that have been ignored so far by this
0N/A * LDIF reader because they did not match the import criteria.
0N/A *
0N/A * @return The total number of entries ignored so far by this LDIF reader.
0N/A */
0N/A public long getEntriesIgnored()
0N/A {
4591N/A return entriesIgnored.get();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the total number of entries rejected so far by this LDIF reader.
0N/A * This includes both entries that were rejected because of internal
0N/A * validation failure (e.g., they didn't conform to the defined server
0N/A * schema) or an external validation failure (e.g., the component using this
0N/A * LDIF reader didn't accept the entry because it didn't have a parent).
0N/A *
0N/A * @return The total number of entries rejected so far by this LDIF reader.
0N/A */
0N/A public long getEntriesRejected()
0N/A {
4591N/A return entriesRejected.get();
0N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse a modifyDN change record entry from LDIF.
165N/A *
165N/A * @param entryDN
165N/A * The name of the entry being modified.
165N/A * @param lines
165N/A * The lines to parse.
165N/A * @return Returns the parsed modifyDN change record entry.
165N/A * @throws LDIFException
165N/A * If there was an error when parsing the change record.
165N/A */
165N/A private ChangeRecordEntry parseModifyDNChangeRecordEntry(DN entryDN,
165N/A LinkedList<StringBuilder> lines) throws LDIFException {
165N/A
165N/A DN newSuperiorDN = null;
4591N/A RDN newRDN;
4591N/A boolean deleteOldRDN;
165N/A
165N/A if(lines.isEmpty())
165N/A {
2086N/A Message message = ERR_LDIF_NO_MOD_DN_ATTRIBUTES.get();
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A
165N/A StringBuilder line = lines.remove();
165N/A String rdnStr = getModifyDNAttributeValue(lines, line, entryDN, "newrdn");
165N/A
165N/A try
165N/A {
165N/A newRDN = RDN.decode(rdnStr);
165N/A } catch (DirectoryException de)
165N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lineNumber, line.toString(), de.getMessageObject());
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A } catch (Exception e)
165N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
2086N/A Message message =
2086N/A ERR_LDIF_INVALID_DN.get(lineNumber, line.toString(), e.getMessage());
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A
165N/A if(lines.isEmpty())
165N/A {
2086N/A Message message = ERR_LDIF_NO_DELETE_OLDRDN_ATTRIBUTE.get();
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A lineNumber++;
165N/A
165N/A line = lines.remove();
165N/A String delStr = getModifyDNAttributeValue(lines, line,
165N/A entryDN, "deleteoldrdn");
165N/A
165N/A if(delStr.equalsIgnoreCase("false") ||
165N/A delStr.equalsIgnoreCase("no") ||
165N/A delStr.equalsIgnoreCase("0"))
165N/A {
165N/A deleteOldRDN = false;
165N/A } else if(delStr.equalsIgnoreCase("true") ||
165N/A delStr.equalsIgnoreCase("yes") ||
165N/A delStr.equalsIgnoreCase("1"))
165N/A {
165N/A deleteOldRDN = true;
165N/A } else
165N/A {
2086N/A Message message = ERR_LDIF_INVALID_DELETE_OLDRDN_ATTRIBUTE.get(delStr);
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A
165N/A if(!lines.isEmpty())
165N/A {
165N/A lineNumber++;
165N/A
165N/A line = lines.remove();
165N/A
165N/A String dnStr = getModifyDNAttributeValue(lines, line,
165N/A entryDN, "newsuperior");
165N/A try
165N/A {
165N/A newSuperiorDN = DN.decode(dnStr);
165N/A } catch (DirectoryException de)
165N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lineNumber, line.toString(), de.getMessageObject());
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A } catch (Exception e)
165N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
2086N/A Message message = ERR_LDIF_INVALID_DN.get(
2086N/A lineNumber, line.toString(), e.getMessage());
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A }
165N/A
402N/A return new ModifyDNChangeRecordEntry(entryDN, newRDN, deleteOldRDN,
402N/A newSuperiorDN);
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Return the string value for the specified attribute name which only
165N/A * has one value.
165N/A *
165N/A * @param lines
165N/A * The set of lines for this change record entry.
165N/A * @param line
165N/A * The line currently being examined.
165N/A * @param entryDN
165N/A * The name of the entry being modified.
165N/A * @param attributeName
165N/A * The attribute name
165N/A * @return the string value for the attribute name.
165N/A * @throws LDIFException
165N/A * If a problem occurs while attempting to determine the
165N/A * attribute value.
165N/A */
165N/A
165N/A private String getModifyDNAttributeValue(LinkedList<StringBuilder> lines,
165N/A StringBuilder line,
165N/A DN entryDN,
165N/A String attributeName) throws LDIFException
165N/A {
165N/A Attribute attr =
165N/A readSingleValueAttribute(lines, line, entryDN, attributeName);
4134N/A return attr.iterator().next().getValue().toString();
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse a modify change record entry from LDIF.
165N/A *
165N/A * @param entryDN
165N/A * The name of the entry being modified.
165N/A * @param lines
165N/A * The lines to parse.
165N/A * @return Returns the parsed modify change record entry.
165N/A * @throws LDIFException
165N/A * If there was an error when parsing the change record.
165N/A */
165N/A private ChangeRecordEntry parseModifyChangeRecordEntry(DN entryDN,
165N/A LinkedList<StringBuilder> lines) throws LDIFException {
165N/A
1177N/A List<RawModification> modifications = new ArrayList<RawModification>();
165N/A while(!lines.isEmpty())
165N/A {
4591N/A ModificationType modType;
165N/A
165N/A StringBuilder line = lines.remove();
165N/A Attribute attr =
165N/A readSingleValueAttribute(lines, line, entryDN, null);
165N/A String name = attr.getName();
165N/A
165N/A // Get the attribute description
4134N/A String attrDescr = attr.iterator().next().getValue().toString();
165N/A
165N/A String lowerName = toLowerCase(name);
3853N/A if (lowerName.equals("add"))
165N/A {
165N/A modType = ModificationType.ADD;
3853N/A }
3853N/A else if (lowerName.equals("delete"))
165N/A {
165N/A modType = ModificationType.DELETE;
3853N/A }
3853N/A else if (lowerName.equals("replace"))
165N/A {
165N/A modType = ModificationType.REPLACE;
3853N/A }
3853N/A else if (lowerName.equals("increment"))
165N/A {
165N/A modType = ModificationType.INCREMENT;
3853N/A }
3853N/A else
165N/A {
165N/A // Invalid attribute name.
3853N/A Message message = ERR_LDIF_INVALID_MODIFY_ATTRIBUTE.get(name,
3853N/A "add, delete, replace, increment");
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A
165N/A // Now go through the rest of the attributes till the "-" line is
165N/A // reached.
165N/A Attribute modAttr = LDIFReader.parseAttrDescription(attrDescr);
3853N/A AttributeBuilder builder = new AttributeBuilder(modAttr, true);
165N/A while (! lines.isEmpty())
165N/A {
165N/A line = lines.remove();
165N/A if(line.toString().equals("-"))
165N/A {
165N/A break;
165N/A }
3853N/A Attribute a = readSingleValueAttribute(lines, line, entryDN, attrDescr);
3853N/A builder.addAll(a);
165N/A }
165N/A
3853N/A LDAPAttribute ldapAttr = new LDAPAttribute(builder.toAttribute());
165N/A LDAPModification mod = new LDAPModification(modType, ldapAttr);
165N/A modifications.add(mod);
165N/A }
165N/A
165N/A return new ModifyChangeRecordEntry(entryDN, modifications);
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse a delete change record entry from LDIF.
165N/A *
165N/A * @param entryDN
165N/A * The name of the entry being deleted.
165N/A * @param lines
165N/A * The lines to parse.
165N/A * @return Returns the parsed delete change record entry.
165N/A * @throws LDIFException
165N/A * If there was an error when parsing the change record.
165N/A */
165N/A private ChangeRecordEntry parseDeleteChangeRecordEntry(DN entryDN,
165N/A LinkedList<StringBuilder> lines) throws LDIFException {
165N/A
868N/A if (!lines.isEmpty())
165N/A {
2086N/A Message message = ERR_LDIF_INVALID_DELETE_ATTRIBUTES.get();
2086N/A throw new LDIFException(message, lineNumber, true);
165N/A }
165N/A
165N/A return new DeleteChangeRecordEntry(entryDN);
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse an add change record entry from LDIF.
165N/A *
165N/A * @param entryDN
165N/A * The name of the entry being added.
165N/A * @param lines
165N/A * The lines to parse.
165N/A * @return Returns the parsed add change record entry.
165N/A * @throws LDIFException
165N/A * If there was an error when parsing the change record.
165N/A */
165N/A private ChangeRecordEntry parseAddChangeRecordEntry(DN entryDN,
165N/A LinkedList<StringBuilder> lines) throws LDIFException {
165N/A
165N/A HashMap<ObjectClass,String> objectClasses =
165N/A new HashMap<ObjectClass,String>();
4495N/A HashMap<AttributeType,List<AttributeBuilder>> attrBuilders =
4495N/A new HashMap<AttributeType, List<AttributeBuilder>>();
165N/A for(StringBuilder line : lines)
165N/A {
165N/A readAttribute(lines, line, entryDN, objectClasses,
4495N/A attrBuilders, attrBuilders, importConfig.validateSchema());
165N/A }
165N/A
165N/A // Reconstruct the object class attribute.
165N/A AttributeType ocType = DirectoryServer.getObjectClassAttributeType();
3853N/A AttributeBuilder builder = new AttributeBuilder(ocType, "objectClass");
165N/A for (String value : objectClasses.values()) {
4134N/A AttributeValue av = AttributeValues.create(ocType, value);
3853N/A builder.add(av);
165N/A }
165N/A List<Attribute> ocAttrList = new ArrayList<Attribute>(1);
3853N/A ocAttrList.add(builder.toAttribute());
4495N/A HashMap<AttributeType,List<Attribute>> attributes =
4495N/A new HashMap<AttributeType, List<Attribute>>(attrBuilders.size());
165N/A attributes.put(ocType, ocAttrList);
165N/A
4495N/A for (Map.Entry<AttributeType, List<AttributeBuilder>>
4495N/A attrTypeEntry : attrBuilders.entrySet())
4495N/A {
4495N/A AttributeType attrType = attrTypeEntry.getKey();
4495N/A List<AttributeBuilder> attrBuilderList = attrTypeEntry.getValue();
4495N/A List<Attribute> attrList =
4495N/A new ArrayList<Attribute>(attrBuilderList.size());
4495N/A for (AttributeBuilder attrBuilder : attrBuilderList)
4495N/A {
4495N/A attrList.add(attrBuilder.toAttribute());
4495N/A }
4495N/A attributes.put(attrType, attrList);
4495N/A }
4495N/A
165N/A return new AddChangeRecordEntry(entryDN, attributes);
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse colon position in an attribute description.
165N/A *
165N/A * @param lines
165N/A * The current set of lines.
165N/A * @param line
165N/A * The current line.
165N/A * @return The colon position.
165N/A * @throws LDIFException
165N/A * If the colon was badly placed or not found.
165N/A */
165N/A private int parseColonPosition(LinkedList<StringBuilder> lines,
165N/A StringBuilder line) throws LDIFException {
165N/A
165N/A int colonPos = line.indexOf(":");
165N/A if (colonPos <= 0)
165N/A {
2086N/A Message message = ERR_LDIF_NO_ATTR_NAME.get(
2086N/A lastEntryLineNumber, line.toString());
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true);
165N/A }
165N/A return colonPos;
165N/A }
165N/A
165N/A
165N/A
165N/A /**
165N/A * Parse a single attribute value from a line of LDIF.
165N/A *
165N/A * @param lines
165N/A * The current set of lines.
165N/A * @param line
165N/A * The current line.
165N/A * @param entryDN
165N/A * The DN of the entry being parsed.
165N/A * @param colonPos
165N/A * The position of the separator colon in the line.
165N/A * @param attrName
165N/A * The name of the attribute being parsed.
165N/A * @return The parsed attribute value.
165N/A * @throws LDIFException
165N/A * If an error occurred when parsing the attribute value.
165N/A */
4134N/A private ByteString parseSingleValue(
165N/A LinkedList<StringBuilder> lines,
165N/A StringBuilder line,
165N/A DN entryDN,
165N/A int colonPos,
165N/A String attrName) throws LDIFException {
165N/A
165N/A // Look at the character immediately after the colon. If there is
165N/A // none, then assume an attribute with an empty value. If it is another
165N/A // colon, then the value must be base64-encoded. If it is a less-than
165N/A // sign, then assume that it is a URL. Otherwise, it is a regular value.
165N/A int length = line.length();
4134N/A ByteString value;
165N/A if (colonPos == (length-1))
165N/A {
4134N/A value = ByteString.empty();
165N/A }
165N/A else
165N/A {
165N/A char c = line.charAt(colonPos+1);
165N/A if (c == ':')
165N/A {
165N/A // The value is base64-encoded. Find the first non-blank
165N/A // character, take the rest of the line, and base64-decode it.
165N/A int pos = colonPos+2;
165N/A while ((pos < length) && (line.charAt(pos) == ' '))
165N/A {
165N/A pos++;
165N/A }
165N/A
165N/A try
165N/A {
4134N/A value = ByteString.wrap(Base64.decode(line.substring(pos)));
165N/A }
165N/A catch (Exception e)
165N/A {
165N/A // The value did not have a valid base64-encoding.
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
165N/A
2086N/A Message message = ERR_LDIF_COULD_NOT_BASE64_DECODE_ATTR.get(
2086N/A String.valueOf(entryDN),
2086N/A lastEntryLineNumber, line,
2086N/A String.valueOf(e));
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
165N/A }
165N/A }
165N/A else if (c == '<')
165N/A {
165N/A // Find the first non-blank character, decode the rest of the
165N/A // line as a URL, and read its contents.
165N/A int pos = colonPos+2;
165N/A while ((pos < length) && (line.charAt(pos) == ' '))
165N/A {
165N/A pos++;
165N/A }
165N/A
165N/A URL contentURL;
165N/A try
165N/A {
165N/A contentURL = new URL(line.substring(pos));
165N/A }
165N/A catch (Exception e)
165N/A {
165N/A // The URL was malformed or had an invalid protocol.
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
165N/A
2086N/A Message message = ERR_LDIF_INVALID_URL.get(String.valueOf(entryDN),
165N/A lastEntryLineNumber,
165N/A String.valueOf(attrName),
165N/A String.valueOf(e));
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
165N/A }
165N/A
165N/A
165N/A InputStream inputStream = null;
4591N/A ByteStringBuilder builder;
165N/A try
165N/A {
4134N/A builder = new ByteStringBuilder();
165N/A inputStream = contentURL.openConnection().getInputStream();
165N/A
165N/A int bytesRead;
165N/A while ((bytesRead = inputStream.read(buffer)) > 0)
165N/A {
4134N/A builder.append(buffer, 0, bytesRead);
165N/A }
165N/A
4134N/A value = builder.toByteString();
165N/A }
165N/A catch (Exception e)
165N/A {
165N/A // We were unable to read the contents of that URL for some
165N/A // reason.
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
165N/A
2086N/A Message message = ERR_LDIF_URL_IO_ERROR.get(String.valueOf(entryDN),
165N/A lastEntryLineNumber,
165N/A String.valueOf(attrName),
165N/A String.valueOf(contentURL),
165N/A String.valueOf(e));
165N/A logToRejectWriter(lines, message);
2086N/A throw new LDIFException(message, lastEntryLineNumber, true, e);
165N/A }
165N/A finally
165N/A {
165N/A if (inputStream != null)
165N/A {
165N/A try
165N/A {
165N/A inputStream.close();
165N/A } catch (Exception e) {}
165N/A }
165N/A }
165N/A }
165N/A else
165N/A {
165N/A // The rest of the line should be the value. Skip over any
165N/A // spaces and take the rest of the line as the value.
165N/A int pos = colonPos+1;
165N/A while ((pos < length) && (line.charAt(pos) == ' '))
165N/A {
165N/A pos++;
165N/A }
165N/A
4134N/A value = ByteString.valueOf(line.substring(pos));
165N/A }
165N/A }
165N/A return value;
165N/A }
165N/A
165N/A /**
165N/A * Log a message to the reject writer if one is configured.
165N/A *
165N/A * @param lines
165N/A * The set of rejected lines.
165N/A * @param message
165N/A * The associated error message.
165N/A */
165N/A private void logToRejectWriter(LinkedList<StringBuilder> lines,
2086N/A Message message) {
165N/A
5852N/A entriesRejected.incrementAndGet();
165N/A BufferedWriter rejectWriter = importConfig.getRejectWriter();
165N/A if (rejectWriter != null)
165N/A {
1773N/A logToWriter(rejectWriter, lines, message);
1773N/A }
1773N/A }
1773N/A
1773N/A /**
1773N/A * Log a message to the reject writer if one is configured.
1773N/A *
1773N/A * @param lines
1773N/A * The set of rejected lines.
1773N/A * @param message
1773N/A * The associated error message.
1773N/A */
1773N/A private void logToSkipWriter(LinkedList<StringBuilder> lines,
2086N/A Message message) {
5852N/A entriesIgnored.incrementAndGet();
1773N/A BufferedWriter skipWriter = importConfig.getSkipWriter();
1773N/A if (skipWriter != null)
1773N/A {
1773N/A logToWriter(skipWriter, lines, message);
1773N/A }
1773N/A }
1773N/A
1773N/A /**
1773N/A * Log a message to the given writer.
1773N/A *
1773N/A * @param writer
1773N/A * The writer to write to.
1773N/A * @param lines
2086N/A * The set of rejected lines.
1773N/A * @param message
1773N/A * The associated error message.
1773N/A */
1773N/A private void logToWriter(BufferedWriter writer,
1773N/A LinkedList<StringBuilder> lines,
2086N/A Message message)
1773N/A {
1773N/A if (writer != null)
1773N/A {
165N/A try
165N/A {
1773N/A writer.write("# ");
2086N/A writer.write(String.valueOf(message));
1773N/A writer.newLine();
165N/A for (StringBuilder sb : lines)
165N/A {
1773N/A writer.write(sb.toString());
1773N/A writer.newLine();
165N/A }
165N/A
1773N/A writer.newLine();
165N/A }
165N/A catch (Exception e)
165N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
165N/A }
165N/A }
165N/A }
1773N/A
4591N/A
5169N/A /**
5169N/A * Adds any missing RDN attributes to the entry that is being imported.
5169N/A */
5169N/A private void addRDNAttributesIfNecessary(DN entryDN,
5169N/A HashMap<AttributeType,List<Attribute>>userAttributes,
5169N/A HashMap<AttributeType,List<Attribute>> operationalAttributes)
5169N/A {
5169N/A RDN rdn = entryDN.getRDN();
5169N/A int numAVAs = rdn.getNumValues();
5169N/A for (int i=0; i < numAVAs; i++)
5169N/A {
5169N/A AttributeType t = rdn.getAttributeType(i);
5169N/A AttributeValue v = rdn.getAttributeValue(i);
5169N/A String n = rdn.getAttributeName(i);
5169N/A if (t.isOperational())
5169N/A {
5169N/A List<Attribute> attrList = operationalAttributes.get(t);
5169N/A if (attrList == null)
5169N/A {
5169N/A attrList = new ArrayList<Attribute>();
5169N/A attrList.add(Attributes.create(t, n, v));
5169N/A operationalAttributes.put(t, attrList);
5169N/A }
5169N/A else
5169N/A {
5169N/A boolean found = false;
5169N/A for (int j = 0; j < attrList.size(); j++)
5169N/A {
5169N/A Attribute a = attrList.get(j);
5169N/A
5169N/A if (a.hasOptions())
5169N/A {
5169N/A continue;
5169N/A }
5169N/A
5169N/A if (!a.contains(v))
5169N/A {
5169N/A AttributeBuilder builder = new AttributeBuilder(a);
5169N/A builder.add(v);
5169N/A attrList.set(j, builder.toAttribute());
5169N/A }
5169N/A
5169N/A found = true;
5169N/A break;
5169N/A }
5169N/A
5169N/A if (!found)
5169N/A {
5169N/A attrList.add(Attributes.create(t, n, v));
5169N/A }
5169N/A }
5169N/A }
5169N/A else
5169N/A {
5169N/A List<Attribute> attrList = userAttributes.get(t);
5169N/A if (attrList == null)
5169N/A {
5169N/A attrList = new ArrayList<Attribute>();
5169N/A attrList.add(Attributes.create(t, n, v));
5169N/A userAttributes.put(t, attrList);
5169N/A }
5169N/A else
5169N/A {
5169N/A boolean found = false;
5169N/A for (int j = 0; j < attrList.size(); j++)
5169N/A {
5169N/A Attribute a = attrList.get(j);
5169N/A
5169N/A if (a.hasOptions())
5169N/A {
5169N/A continue;
5169N/A }
5169N/A
5169N/A if (!a.contains(v))
5169N/A {
5169N/A AttributeBuilder builder = new AttributeBuilder(a);
5169N/A builder.add(v);
5169N/A attrList.set(j, builder.toAttribute());
5169N/A }
5169N/A
5169N/A found = true;
5169N/A break;
5169N/A }
5169N/A
5169N/A if (!found)
5169N/A {
5169N/A attrList.add(Attributes.create(t, n, v));
5169N/A }
5169N/A }
5169N/A }
5169N/A }
5169N/A }
0N/A}
0N/A