mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
03.25.2015 d436398b7fc0b5642a8b0f5c77cab4accec9c056
(CR-5887) Decouple LDIFReader from JE's import

Currently LDIFReader depends on JE's importLDIF classes.
This will become a problem when trying to build JE as a separate jar from OpenDJ zip.
This is already a problem with pluggable backends because it means we would need to make LDIFReader also depend on the pluggable import package, which is not acceptable.


Extracted ImportLDIFReader as a subclass of LDIFReader to receive all import related code + used the former in Importer class.
1 files added
2 files modified
847 ■■■■ changed files
opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/ImportLDIFReader.java 275 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java 5 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/util/LDIFReader.java 567 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/ImportLDIFReader.java
New file
@@ -0,0 +1,275 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *      Copyright 2015 ForgeRock AS
 */
package org.opends.server.backends.jeb.importLDIF;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.util.Reject;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.backends.jeb.EntryID;
import org.opends.server.backends.jeb.RootContainer;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.ObjectClass;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFReader;
/**
 * This class specializes the LDIFReader for imports.
 */
public final class ImportLDIFReader extends LDIFReader
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private final RootContainer rootContainer;
  /**
   * Creates a new LDIF reader that will read information from the specified file.
   *
   * @param importConfig
   *          The import configuration for this LDIF reader. It must not be <CODE>null</CODE>.
   * @param rootContainer
   *          The root container needed to get the next entry ID.
   * @throws IOException
   *           If a problem occurs while opening the LDIF file for reading.
   */
  public ImportLDIFReader(LDIFImportConfig importConfig, RootContainer rootContainer) throws IOException
  {
    super(importConfig);
    Reject.ifNull(importConfig, rootContainer);
    this.rootContainer = rootContainer;
  }
  /**
   * Reads the next entry from the LDIF source.
   *
   * @return The next entry read from the LDIF source, or <CODE>null</CODE> if the end of the LDIF
   *         data is reached.
   * @param suffixesMap
   *          A map of suffixes instances.
   * @param entryInfo
   *          A object to hold information about the entry ID and what suffix was selected.
   * @throws IOException
   *           If an I/O problem occurs while reading from the file.
   * @throws LDIFException
   *           If the information read cannot be parsed as an LDIF entry.
   */
  public final Entry readEntry(Map<DN, Suffix> suffixesMap, Importer.EntryInformation entryInfo) throws IOException,
      LDIFException
  {
    final boolean checkSchema = importConfig.validateSchema();
    while (true)
    {
      LinkedList<StringBuilder> lines;
      DN entryDN;
      EntryID entryID;
      Suffix suffix;
      synchronized (this)
      {
        // Read the set of lines that make up the next entry.
        lines = readEntryLines();
        if (lines == null)
        {
          return null;
        }
        lastEntryBodyLines = lines;
        lastEntryHeaderLines = new LinkedList<StringBuilder>();
        // Read the DN of the entry and see if it is one that should be included
        // in the import.
        try
        {
          entryDN = readDN(lines);
        }
        catch (LDIFException e)
        {
          logger.traceException(e);
          continue;
        }
        if (entryDN == null)
        {
          // This should only happen if the LDIF starts with the "version:" line
          // and has a blank line immediately after that. In that case, simply
          // read and return the next entry.
          continue;
        }
        else if (!importConfig.includeEntry(entryDN))
        {
          logger.trace("Skipping entry %s because the DN is not one that "
              + "should be included based on the include and exclude branches.", entryDN);
          entriesRead.incrementAndGet();
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          continue;
        }
        suffix = Importer.getMatchSuffix(entryDN, suffixesMap);
        if (suffix == null)
        {
          logger.trace("Skipping entry %s because the DN is not one that "
              + "should be included based on a suffix match check.", entryDN);
          entriesRead.incrementAndGet();
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          continue;
        }
        entriesRead.incrementAndGet();
        entryID = rootContainer.getNextEntryID();
        suffix.addPending(entryDN);
      }
      // Create the entry and see if it is one that should be included in the import
      final Entry entry = createEntry(lines, entryDN, checkSchema, suffix);
      if (entry == null
          || !isIncludedInImport(entry, suffix, lines)
          || !invokeImportPlugins(entry, suffix, lines)
          || (checkSchema && !isValidAgainstSchema(entry, suffix, lines)))
      {
        continue;
      }
      entryInfo.setEntryID(entryID);
      entryInfo.setSuffix(suffix);
      // The entry should be included in the import, so return it.
      return entry;
    }
  }
  private Entry createEntry(List<StringBuilder> lines, DN entryDN, boolean checkSchema, Suffix suffix)
  {
    // Read the set of attributes from the entry.
    Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
    Map<AttributeType, List<AttributeBuilder>> userAttrBuilders =
        new HashMap<AttributeType, List<AttributeBuilder>>();
    Map<AttributeType, List<AttributeBuilder>> operationalAttrBuilders =
        new HashMap<AttributeType, List<AttributeBuilder>>();
    try
    {
      for (StringBuilder line : lines)
      {
        readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders, operationalAttrBuilders, checkSchema);
      }
    }
    catch (LDIFException e)
    {
      if (logger.isTraceEnabled())
      {
        logger.trace("Skipping entry %s because reading" + "its attributes failed.", entryDN);
      }
      logToSkipWriter(lines, ERR_LDIF_READ_ATTR_SKIP.get(entryDN, e.getMessage()));
      suffix.removePending(entryDN);
      return null;
    }
    final Entry entry = new Entry(entryDN, objectClasses,
        toAttributesMap(userAttrBuilders), toAttributesMap(operationalAttrBuilders));
    logger.trace("readEntry(), created entry: %s", entry);
    return entry;
  }
  private boolean isIncludedInImport(Entry entry, Suffix suffix, LinkedList<StringBuilder> lines)
  {
    final DN entryDN = entry.getName();
    try
    {
      if (!importConfig.includeEntry(entry))
      {
        if (logger.isTraceEnabled())
        {
          logger.trace("Skipping entry %s because the DN is not one that "
              + "should be included based on the include and exclude filters.", entryDN);
        }
        logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
        suffix.removePending(entryDN);
        return false;
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      suffix.removePending(entryDN);
      logToSkipWriter(lines,
          ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.get(entry.getName(), lastEntryLineNumber, e));
      suffix.removePending(entryDN);
      return false;
    }
    return true;
  }
  private boolean invokeImportPlugins(final Entry entry, Suffix suffix, LinkedList<StringBuilder> lines)
  {
    if (importConfig.invokeImportPlugins())
    {
      PluginResult.ImportLDIF pluginResult = pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
      if (!pluginResult.continueProcessing())
      {
        final DN entryDN = entry.getName();
        LocalizableMessage m;
        LocalizableMessage rejectMessage = pluginResult.getErrorMessage();
        if (rejectMessage == null)
        {
          m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(entryDN);
        }
        else
        {
          m = ERR_LDIF_REJECTED_BY_PLUGIN.get(entryDN, rejectMessage);
        }
        logToRejectWriter(lines, m);
        suffix.removePending(entryDN);
        return false;
      }
    }
    return true;
  }
  private boolean isValidAgainstSchema(Entry entry, Suffix suffix, LinkedList<StringBuilder> lines)
  {
    final DN entryDN = entry.getName();
    addRDNAttributesIfNecessary(entryDN, entry.getUserAttributes(), entry.getOperationalAttributes());
    // Add any superior objectclass(s) missing in the objectclass map.
    addSuperiorObjectClasses(entry.getObjectClasses());
    LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
    if (!entry.conformsToSchema(null, false, true, false, invalidReason))
    {
      LocalizableMessage message = ERR_LDIF_SCHEMA_VIOLATION.get(entryDN, lastEntryLineNumber, invalidReason);
      logToRejectWriter(lines, message);
      suffix.removePending(entryDN);
      return false;
    }
    return true;
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java
@@ -61,7 +61,6 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.*;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.Platform;
import org.opends.server.util.StaticUtils;
@@ -141,7 +140,7 @@
  private final LocalDBBackendCfg backendConfiguration;
  /** LDIF reader. */
  private LDIFReader reader;
  private ImportLDIFReader reader;
  /** Migrated entry count. */
  private int migratedCount;
@@ -853,7 +852,7 @@
    try {
      try
      {
        reader = new LDIFReader(importConfiguration, rootContainer);
        reader = new ImportLDIFReader(importConfiguration, rootContainer);
      }
      catch (IOException ioe)
      {
opendj3-server-dev/src/server/org/opends/server/util/LDIFReader.java
@@ -22,13 +22,25 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2012-2014 ForgeRock AS
 *      Portions Copyright 2012-2015 ForgeRock AS
 */
package org.opends.server.util;
import java.io.*;
import static org.forgerock.util.Reject.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.forgerock.i18n.LocalizableMessage;
@@ -38,20 +50,22 @@
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ModificationType;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.backends.jeb.EntryID;
import org.opends.server.backends.jeb.RootContainer;
import org.opends.server.backends.jeb.importLDIF.Importer;
import org.opends.server.backends.jeb.importLDIF.Suffix;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPModification;
import org.opends.server.types.*;
import static org.forgerock.util.Reject.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.types.AcceptRejectWarn;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.RawModification;
/**
 * This class provides the ability to read information from an LDIF file.  It
@@ -63,7 +77,7 @@
     mayInstantiate=true,
     mayExtend=false,
     mayInvoke=true)
public final class LDIFReader implements Closeable
public class LDIFReader implements Closeable
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -71,16 +85,16 @@
  private BufferedReader reader;
  /** The import configuration that specifies what should be imported. */
  private LDIFImportConfig importConfig;
  protected LDIFImportConfig importConfig;
  /** The lines that comprise the body of the last entry read. */
  private List<StringBuilder> lastEntryBodyLines;
  protected List<StringBuilder> lastEntryBodyLines;
  /**
   * The lines that comprise the header (DN and any comments) for the last entry
   * read.
   */
  private List<StringBuilder> lastEntryHeaderLines;
  protected List<StringBuilder> lastEntryHeaderLines;
  /**
@@ -94,13 +108,13 @@
   * those that were ignored because they didn't match the criteria, and
   * including those that were rejected because they were invalid in some way.
   */
  private final AtomicLong entriesRead = new AtomicLong();
  protected final AtomicLong entriesRead = new AtomicLong();
  /** The number of entries that have been rejected by this LDIF reader. */
  private final AtomicLong entriesRejected = new AtomicLong();
  /** The line number on which the last entry started. */
  private long lastEntryLineNumber;
  protected long lastEntryLineNumber = -1;
  /**
   * The line number of the last line read from the LDIF file, starting with 1.
@@ -111,10 +125,7 @@
   * The plugin config manager that will be used if we are to invoke plugins on
   * the entries as they are read.
   */
  private PluginConfigManager pluginConfigManager;
  private RootContainer rootContainer;
  protected PluginConfigManager pluginConfigManager;
  /**
   * Creates a new LDIF reader that will read information from the specified
@@ -133,8 +144,6 @@
    this.importConfig = importConfig;
    reader               = importConfig.getReader();
    lineNumber           = 0;
    lastEntryLineNumber  = -1;
    lastEntryBodyLines   = new LinkedList<StringBuilder>();
    lastEntryHeaderLines = new LinkedList<StringBuilder>();
    pluginConfigManager  = DirectoryServer.getPluginConfigManager();
@@ -148,42 +157,6 @@
  /**
   * Creates a new LDIF reader that will read information from the
   * specified file.
   *
   * @param importConfig
   *          The import configuration for this LDIF reader. It must not
   *          be <CODE>null</CODE>.
   * @param rootContainer The root container needed to get the next entry ID.
   *
   * @throws IOException
   *           If a problem occurs while opening the LDIF file for
   *           reading.
   */
  public LDIFReader(LDIFImportConfig importConfig, RootContainer rootContainer)
         throws IOException
  {
    ifNull(importConfig);
    this.importConfig = importConfig;
    this.reader               = importConfig.getReader();
    this.lineNumber           = 0;
    this.lastEntryLineNumber  = -1;
    this.lastEntryBodyLines   = new LinkedList<StringBuilder>();
    this.lastEntryHeaderLines = new LinkedList<StringBuilder>();
    this.pluginConfigManager  = DirectoryServer.getPluginConfigManager();
    this.rootContainer = rootContainer;
    // If we should invoke import plugins, then do so.
    if (importConfig.invokeImportPlugins())
    {
      // Inform LDIF import plugins that an import session is ending
      this.pluginConfigManager.invokeLDIFImportBeginPlugins(importConfig);
    }
  }
  /**
   * Reads the next entry from the LDIF source.
   *
   * @return  The next entry read from the LDIF source, or <CODE>null</CODE> if
@@ -205,240 +178,6 @@
  /**
   * Reads the next entry from the LDIF source.
   *
   * @return  The next entry read from the LDIF source, or <CODE>null</CODE> if
   *          the end of the LDIF data is reached.
   *
   * @param map  A map of suffixes instances.
   *
   * @param entryInfo A object to hold information about the entry ID and what
   *                  suffix was selected.
   *
   * @throws  IOException  If an I/O problem occurs while reading from the file.
   *
   * @throws  LDIFException  If the information read cannot be parsed as an LDIF
   *                         entry.
   */
  public final Entry readEntry(Map<DN, Suffix> map,
                               Importer.EntryInformation entryInfo)
         throws IOException, LDIFException
  {
    return readEntry(importConfig.validateSchema(), map, entryInfo);
  }
  private  Entry readEntry(boolean checkSchema, Map<DN, Suffix> map,
                                Importer.EntryInformation entryInfo)
          throws IOException, LDIFException
  {
    while (true)
    {
      LinkedList<StringBuilder> lines;
      DN entryDN;
      EntryID entryID;
      Suffix suffix;
      synchronized (this)
      {
        // Read the set of lines that make up the next entry.
        lines = readEntryLines();
        if (lines == null)
        {
          return null;
        }
        lastEntryBodyLines   = lines;
        lastEntryHeaderLines = new LinkedList<StringBuilder>();
        // Read the DN of the entry and see if it is one that should be included
        // in the import.
        try
        {
          entryDN = readDN(lines);
        } catch (LDIFException le) {
          continue;
        }
        if (entryDN == null)
        {
          // This should only happen if the LDIF starts with the "version:" line
          // and has a blank line immediately after that.  In that case, simply
          // read and return the next entry.
          continue;
        }
        else if (!importConfig.includeEntry(entryDN))
        {
          if (logger.isTraceEnabled())
          {
            logger.trace("Skipping entry %s because the DN isn't" +
                    "one that should be included based on the include and " +
                    "exclude branches.", entryDN);
          }
          entriesRead.incrementAndGet();
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          continue;
        }
        entryID = rootContainer.getNextEntryID();
        suffix = Importer.getMatchSuffix(entryDN, map);
        if(suffix == null)
        {
          if (logger.isTraceEnabled())
          {
            logger.trace("Skipping entry %s because the DN isn't" +
                    "one that should be included based on a suffix match" +
                    "check." ,entryDN);
          }
          entriesRead.incrementAndGet();
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          continue;
        }
        entriesRead.incrementAndGet();
        suffix.addPending(entryDN);
      }
      // Read the set of attributes from the entry.
      Map<ObjectClass, String> objectClasses =
           new HashMap<ObjectClass,String>();
      Map<AttributeType, List<AttributeBuilder>> userAttrBuilders =
           new HashMap<AttributeType,List<AttributeBuilder>>();
      Map<AttributeType, List<AttributeBuilder>> operationalAttrBuilders =
           new HashMap<AttributeType,List<AttributeBuilder>>();
      try
      {
        for (StringBuilder line : lines)
        {
          readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders,
                        operationalAttrBuilders, checkSchema);
        }
      }
      catch (LDIFException e)
      {
        if (logger.isTraceEnabled())
        {
          logger.trace("Skipping entry %s because reading" +
                  "its attributes failed.", entryDN);
        }
        logToSkipWriter(lines, ERR_LDIF_READ_ATTR_SKIP.get(entryDN, e.getMessage()));
        suffix.removePending(entryDN);
        continue;
      }
      // Create the entry and see if it is one that should be included in the
      // import.
      Map<AttributeType, List<Attribute>> userAttributes =
          toAttributesMap(userAttrBuilders);
      Map<AttributeType, List<Attribute>> operationalAttributes =
          toAttributesMap(operationalAttrBuilders);
      Entry entry =  new Entry(entryDN, objectClasses, userAttributes,
                               operationalAttributes);
      logger.trace("readEntry1(), created entry: %s", entry);
      try
      {
        if (! importConfig.includeEntry(entry))
        {
          if (logger.isTraceEnabled())
          {
            logger.trace("Skipping entry %s because the DN is not one " +
                "that should be included based on the include and exclude " +
                "filters.", entryDN);
          }
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          suffix.removePending(entryDN);
          continue;
        }
      }
      catch (Exception e)
      {
        logger.traceException(e);
        suffix.removePending(entryDN);
        logToSkipWriter(lines,
            ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.get(entry.getName(), lastEntryLineNumber, e));
        suffix.removePending(entryDN);
        continue;
      }
      // If we should invoke import plugins, then do so.
      if (importConfig.invokeImportPlugins())
      {
        PluginResult.ImportLDIF pluginResult =
             pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
        if (! pluginResult.continueProcessing())
        {
          LocalizableMessage m;
          LocalizableMessage rejectMessage = pluginResult.getErrorMessage();
          if (rejectMessage == null)
          {
            m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(entryDN);
          }
          else
          {
            m = ERR_LDIF_REJECTED_BY_PLUGIN.get(entryDN, rejectMessage);
          }
          logToRejectWriter(lines, m);
          suffix.removePending(entryDN);
          continue;
        }
      }
      // Make sure that the entry is valid as per the server schema if it is
      // appropriate to do so.
      if (checkSchema)
      {
        //Add the RDN attributes.
        addRDNAttributesIfNecessary(entryDN,userAttributes,
                operationalAttributes);
        //Add any superior objectclass(s) missing in the objectclass map.
        addSuperiorObjectClasses(objectClasses);
        LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
        if (! entry.conformsToSchema(null, false, true, false, invalidReason))
        {
          LocalizableMessage message = ERR_LDIF_SCHEMA_VIOLATION.get(
              entryDN, lastEntryLineNumber, invalidReason);
          logToRejectWriter(lines, message);
          suffix.removePending(entryDN);
          continue;
        }
      }
      entryInfo.setEntryID(entryID);
      entryInfo.setSuffix(suffix);
      // The entry should be included in the import, so return it.
      return entry;
    }
  }
  private Map<AttributeType, List<Attribute>> toAttributesMap(
      Map<AttributeType, List<AttributeBuilder>> attrBuilders)
  {
    Map<AttributeType, List<Attribute>> attributes =
        new HashMap<AttributeType, List<Attribute>>(attrBuilders.size());
    for (Map.Entry<AttributeType, List<AttributeBuilder>>
      attrTypeEntry : attrBuilders.entrySet())
    {
      AttributeType attrType = attrTypeEntry.getKey();
      List<Attribute> attrList = toAttributesList(attrTypeEntry.getValue());
      attributes.put(attrType, attrList);
    }
    return attributes;
  }
  private List<Attribute> toAttributesList(List<AttributeBuilder> builders)
  {
    List<Attribute> results = new ArrayList<Attribute>(builders.size());
    for (AttributeBuilder builder : builders)
    {
      results.add(builder.toAttribute());
    }
    return results;
  }
  /**
   * Reads the next entry from the LDIF source.
   *
   * @param  checkSchema  Indicates whether this reader should perform schema
   *                      checking on the entry before returning it to the
   *                      caller.  Note that some basic schema checking (like
@@ -481,12 +220,8 @@
      }
      else if (!importConfig.includeEntry(entryDN))
      {
        if (logger.isTraceEnabled())
        {
          logger.trace("Skipping entry %s because the DN is not one that " +
              "should be included based on the include and exclude branches.",
                    entryDN);
        }
        logger.trace("Skipping entry %s because the DN is not one that "
            + "should be included based on the include and exclude branches.", entryDN);
        entriesRead.incrementAndGet();
        logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
        continue;
@@ -496,99 +231,142 @@
        entriesRead.incrementAndGet();
      }
      // Read the set of attributes from the entry.
      Map<ObjectClass,String> objectClasses =
           new HashMap<ObjectClass,String>();
      Map<AttributeType,List<AttributeBuilder>> userAttrBuilders =
           new HashMap<AttributeType,List<AttributeBuilder>>();
      Map<AttributeType,List<AttributeBuilder>> operationalAttrBuilders =
           new HashMap<AttributeType,List<AttributeBuilder>>();
      for (StringBuilder line : lines)
      // Create the entry and see if it is one that should be included in the import.
      final Entry entry = createEntry(entryDN, lines, checkSchema);
      if (!isIncludedInImport(entry,lines)
          || !invokeImportPlugins(entry, lines))
      {
        readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders,
            operationalAttrBuilders, checkSchema);
        continue;
      }
      // Create the entry and see if it is one that should be included in the
      // import.
      Map<AttributeType, List<Attribute>> userAttributes =
          toAttributesMap(userAttrBuilders);
      Map<AttributeType, List<Attribute>> operationalAttributes =
          toAttributesMap(operationalAttrBuilders);
      Entry entry =  new Entry(entryDN, objectClasses, userAttributes,
                               operationalAttributes);
      logger.trace("readEntry2(), created entry: %s", entry);
      try
      {
        if (! importConfig.includeEntry(entry))
        {
          if (logger.isTraceEnabled())
          {
            logger.trace("Skipping entry %s because the DN is not one " +
                "that should be included based on the include and exclude " +
                "filters.", entryDN);
          }
          logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
          continue;
        }
      }
      catch (Exception e)
      {
        logger.traceException(e);
        LocalizableMessage message = ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.
            get(entry.getName(), lastEntryLineNumber, e);
        throw new LDIFException(message, lastEntryLineNumber, true, e);
      }
      // If we should invoke import plugins, then do so.
      if (importConfig.invokeImportPlugins())
      {
        PluginResult.ImportLDIF pluginResult =
             pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
        if (! pluginResult.continueProcessing())
        {
          LocalizableMessage m;
          LocalizableMessage rejectMessage = pluginResult.getErrorMessage();
          if (rejectMessage == null)
          {
            m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(entryDN);
          }
          else
          {
            m = ERR_LDIF_REJECTED_BY_PLUGIN.get(entryDN, rejectMessage);
          }
          logToRejectWriter(lines, m);
          continue;
        }
      }
      // Make sure that the entry is valid as per the server schema if it is
      // appropriate to do so.
      if (checkSchema)
      {
        LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
        if (! entry.conformsToSchema(null, false, true, false, invalidReason))
        {
          LocalizableMessage message = ERR_LDIF_SCHEMA_VIOLATION.get(
              entryDN, lastEntryLineNumber, invalidReason);
          logToRejectWriter(lines, message);
          throw new LDIFException(message, lastEntryLineNumber, true);
        }
        //Add any superior objectclass(s) missing in an entries objectclass map.
        addSuperiorObjectClasses(entry.getObjectClasses());
      }
      validateAgainstSchemaIfNeeded(checkSchema, entry, lines);
      // The entry should be included in the import, so return it.
      return entry;
    }
  }
  private Entry createEntry(DN entryDN, List<StringBuilder> lines, boolean checkSchema) throws LDIFException
  {
    Map<ObjectClass, String> objectClasses = new HashMap<ObjectClass, String>();
    Map<AttributeType, List<AttributeBuilder>> userAttrBuilders = new HashMap<AttributeType, List<AttributeBuilder>>();
    Map<AttributeType, List<AttributeBuilder>> operationalAttrBuilders =
        new HashMap<AttributeType, List<AttributeBuilder>>();
    for (StringBuilder line : lines)
    {
      readAttribute(lines, line, entryDN, objectClasses, userAttrBuilders, operationalAttrBuilders, checkSchema);
    }
    final Entry entry = new Entry(entryDN, objectClasses,
        toAttributesMap(userAttrBuilders), toAttributesMap(operationalAttrBuilders));
    logger.trace("readEntry(), created entry: %s", entry);
    return entry;
  }
  private boolean isIncludedInImport(Entry entry, LinkedList<StringBuilder> lines) throws LDIFException
  {
    try
    {
      if (!importConfig.includeEntry(entry))
      {
        final DN entryDN = entry.getName();
        logger.trace("Skipping entry %s because the DN is not one that "
            + "should be included based on the include and exclude filters.", entryDN);
        logToSkipWriter(lines, ERR_LDIF_SKIP.get(entryDN));
        return false;
      }
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message =
          ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_IMPORT.get(entry.getName(), lastEntryLineNumber, e);
      throw new LDIFException(message, lastEntryLineNumber, true, e);
    }
    return true;
  }
  private boolean invokeImportPlugins(Entry entry, LinkedList<StringBuilder> lines)
  {
    if (importConfig.invokeImportPlugins())
    {
      PluginResult.ImportLDIF pluginResult =
          pluginConfigManager.invokeLDIFImportPlugins(importConfig, entry);
      if (!pluginResult.continueProcessing())
      {
        final DN entryDN = entry.getName();
        LocalizableMessage m;
        LocalizableMessage rejectMessage = pluginResult.getErrorMessage();
        if (rejectMessage == null)
        {
          m = ERR_LDIF_REJECTED_BY_PLUGIN_NOMESSAGE.get(entryDN);
        }
        else
        {
          m = ERR_LDIF_REJECTED_BY_PLUGIN.get(entryDN, rejectMessage);
        }
        logToRejectWriter(lines, m);
        return false;
      }
    }
    return true;
  }
  private void validateAgainstSchemaIfNeeded(boolean checkSchema, final Entry entry, LinkedList<StringBuilder> lines)
      throws LDIFException
  {
    if (checkSchema)
    {
      LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
      if (!entry.conformsToSchema(null, false, true, false, invalidReason))
      {
        final DN entryDN = entry.getName();
        LocalizableMessage message = ERR_LDIF_SCHEMA_VIOLATION.get(entryDN, lastEntryLineNumber, invalidReason);
        logToRejectWriter(lines, message);
        throw new LDIFException(message, lastEntryLineNumber, true);
      }
      // Add any superior objectclass(s) missing in an entries objectclass map.
      addSuperiorObjectClasses(entry.getObjectClasses());
    }
  }
  /**
   * Returns a new Map where the provided Map with AttributeBuilders is converted to another Map
   * with Attributes.
   *
   * @param attrBuilders
   *          the provided Map containing AttributeBuilders
   * @return a new Map containing Attributes
   */
  protected Map<AttributeType, List<Attribute>> toAttributesMap(Map<AttributeType, List<AttributeBuilder>> attrBuilders)
  {
    Map<AttributeType, List<Attribute>> attributes = new HashMap<AttributeType, List<Attribute>>(attrBuilders.size());
    for (Map.Entry<AttributeType, List<AttributeBuilder>> attrTypeEntry : attrBuilders.entrySet())
    {
      AttributeType attrType = attrTypeEntry.getKey();
      List<Attribute> attrList = toAttributesList(attrTypeEntry.getValue());
      attributes.put(attrType, attrList);
    }
    return attributes;
  }
  /**
   * Converts the provided List of AttributeBuilders to a new list of Attributes.
   *
   * @param builders the list of AttributeBuilders
   * @return a new list of Attributes
   */
  protected List<Attribute> toAttributesList(List<AttributeBuilder> builders)
  {
    List<Attribute> results = new ArrayList<Attribute>(builders.size());
    for (AttributeBuilder builder : builders)
    {
      results.add(builder.toAttribute());
    }
    return results;
  }
  /**
   * Reads the next change record from the LDIF source.
   *
@@ -684,8 +462,7 @@
   *
   * @throws  LDIFException  If the information read is not valid LDIF.
   */
  private LinkedList<StringBuilder> readEntryLines()
          throws IOException, LDIFException
  protected LinkedList<StringBuilder> readEntryLines() throws IOException, LDIFException
  {
    // Read the entry lines into a buffer.
    LinkedList<StringBuilder> lines = new LinkedList<StringBuilder>();
@@ -789,8 +566,7 @@
   *                         second after the LDIF version), or if a problem
   *                         occurs while trying to parse it.
   */
  private DN readDN(LinkedList<StringBuilder> lines)
          throws LDIFException
  protected DN readDN(LinkedList<StringBuilder> lines) throws LDIFException
  {
    if (lines.isEmpty())
    {
@@ -1011,7 +787,7 @@
   * @throws  LDIFException  If a problem occurs while trying to decode the
   *                         attribute contained in the provided entry.
   */
  private void readAttribute(List<StringBuilder> lines,
  protected void readAttribute(List<StringBuilder> lines,
       StringBuilder line, DN entryDN,
       Map<ObjectClass,String> objectClasses,
       Map<AttributeType,List<AttributeBuilder>> userAttrBuilders,
@@ -1846,7 +1622,7 @@
   * @param message
   *          The associated error message.
   */
  private void logToRejectWriter(List<StringBuilder> lines, LocalizableMessage message)
  protected void logToRejectWriter(List<StringBuilder> lines, LocalizableMessage message)
  {
    entriesRejected.incrementAndGet();
    BufferedWriter rejectWriter = importConfig.getRejectWriter();
@@ -1864,7 +1640,7 @@
   * @param message
   *          The associated error message.
   */
  private void logToSkipWriter(List<StringBuilder> lines, LocalizableMessage message)
  protected void logToSkipWriter(List<StringBuilder> lines, LocalizableMessage message)
  {
    entriesIgnored.incrementAndGet();
    BufferedWriter skipWriter = importConfig.getSkipWriter();
@@ -1912,8 +1688,11 @@
  /**
   * Adds any missing RDN attributes to the entry that is being imported.
   * @param entryDN the entry DN
   * @param userAttributes the user attributes
   * @param operationalAttributes the operational attributes
   */
  private void addRDNAttributesIfNecessary(DN entryDN,
  protected void addRDNAttributesIfNecessary(DN entryDN,
          Map<AttributeType,List<Attribute>>userAttributes,
          Map<AttributeType,List<Attribute>> operationalAttributes)
  {