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 *
6983N/A * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
6983N/A * or http://forgerock.org/license/CDDLv1.0.html.
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
6983N/A * file and include the License file at legal-notices/CDDLv1_0.txt.
6983N/A * If applicable, add the following below this CDDL HEADER, with the
6983N/A * fields enclosed by brackets "[]" replaced with your own identifying
6983N/A * information:
0N/A * Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A *
0N/A *
4361N/A * Copyright 2006-2009 Sun Microsystems, Inc.
6059N/A * Portions Copyright 2013 ForgeRock AS.
0N/A */
0N/Apackage org.opends.server.tools;
3758N/Aimport java.io.BufferedReader;
3758N/Aimport java.io.FileReader;
0N/Aimport java.io.IOException;
1795N/Aimport java.io.OutputStream;
1795N/Aimport java.io.PrintStream;
3758N/Aimport java.util.Collection;
3758N/Aimport java.util.HashSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.LinkedHashSet;
0N/Aimport java.util.LinkedList;
0N/Aimport java.util.List;
3758N/Aimport java.util.ListIterator;
0N/Aimport java.util.TreeMap;
0N/A
6059N/Aimport org.opends.messages.Message;
0N/Aimport org.opends.server.core.DirectoryServer;
421N/Aimport org.opends.server.extensions.ConfigFileHandler;
6064N/Aimport org.opends.server.types.Attribute;
6064N/Aimport org.opends.server.types.AttributeBuilder;
6064N/Aimport org.opends.server.types.AttributeType;
6064N/Aimport org.opends.server.types.AttributeValue;
6064N/Aimport org.opends.server.types.AttributeValues;
6064N/Aimport org.opends.server.types.DN;
6064N/Aimport org.opends.server.types.DirectoryException;
6064N/Aimport org.opends.server.types.Entry;
6064N/Aimport org.opends.server.types.ExistingFileBehavior;
6064N/Aimport org.opends.server.types.LDIFExportConfig;
6064N/Aimport org.opends.server.types.LDIFImportConfig;
6064N/Aimport org.opends.server.types.Modification;
6064N/Aimport org.opends.server.types.ModificationType;
6064N/Aimport org.opends.server.types.NullOutputStream;
6064N/Aimport org.opends.server.types.ObjectClass;
0N/Aimport org.opends.server.util.LDIFReader;
0N/Aimport org.opends.server.util.LDIFWriter;
0N/Aimport org.opends.server.util.args.ArgumentException;
0N/Aimport org.opends.server.util.args.ArgumentParser;
0N/Aimport org.opends.server.util.args.BooleanArgument;
0N/Aimport org.opends.server.util.args.StringArgument;
0N/A
2086N/Aimport static org.opends.messages.ToolMessages.*;
6059N/Aimport static org.opends.server.protocols.ldap.LDAPResultCode.*;
1060N/Aimport static org.opends.server.tools.ToolConstants.*;
6059N/Aimport static org.opends.server.util.ServerConstants.*;
1727N/Aimport static org.opends.server.util.StaticUtils.*;
0N/A
0N/A
0N/A
0N/A/**
0N/A * This class provides a program that may be used to determine the differences
0N/A * between two LDIF files, generating the output in LDIF change format. There
0N/A * are several things to note about the operation of this program:
0N/A * <BR>
0N/A * <UL>
0N/A * <LI>This program is only designed for cases in which both LDIF files to be
0N/A * compared will fit entirely in memory at the same time.</LI>
0N/A * <LI>This program will only compare live data in the LDIF files and will
0N/A * ignore comments and other elements that do not have any real impact on
0N/A * the way that the data is interpreted.</LI>
0N/A * <LI>The differences will be generated in such a way as to provide the
0N/A * maximum amount of information, so that there will be enough information
0N/A * for the changes to be reversed (i.e., it will not use the "replace"
0N/A * modification type but only the "add" and "delete" types, and contents
0N/A * of deleted entries will be included as comments).</LI>
0N/A * </UL>
0N/A *
0N/A *
0N/A * Note
0N/A * that this is only an option for cases in which both LDIF files can fit in
0N/A * memory. Also note that this will only compare live data in the LDIF files
0N/A * and will ignore comments and other elements that do not have any real impact
0N/A * on the way that the data is interpreted.
0N/A */
0N/Apublic class LDIFDiff
0N/A{
0N/A /**
868N/A * The fully-qualified name of this class.
0N/A */
0N/A private static final String CLASS_NAME = "org.opends.server.tools.LDIFDiff";
0N/A
0N/A
0N/A
0N/A /**
0N/A * Provides the command line arguments to the <CODE>mainDiff</CODE> method
0N/A * so that they can be processed.
0N/A *
0N/A * @param args The command line arguments provided to this program.
0N/A */
0N/A public static void main(String[] args)
0N/A {
1795N/A int exitCode = mainDiff(args, false, System.out, System.err);
0N/A if (exitCode != 0)
0N/A {
1727N/A System.exit(filterExitCode(exitCode));
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Parses the provided command line arguments and performs the appropriate
0N/A * LDIF diff operation.
0N/A *
781N/A * @param args The command line arguments provided to this
781N/A * program.
781N/A * @param serverInitialized Indicates whether the Directory Server has
781N/A * already been initialized (and therefore should
781N/A * not be initialized a second time).
1795N/A * @param outStream The output stream to use for standard output, or
1795N/A * {@code null} if standard output is not needed.
1795N/A * @param errStream The output stream to use for standard error, or
1795N/A * {@code null} if standard error is not needed.
0N/A *
0N/A * @return The return code for this operation. A value of zero indicates
0N/A * that all processing completed successfully. A nonzero value
0N/A * indicates that some problem occurred during processing.
0N/A */
1795N/A public static int mainDiff(String[] args, boolean serverInitialized,
1795N/A OutputStream outStream, OutputStream errStream)
0N/A {
6459N/A PrintStream out = NullOutputStream.wrapOrNullStream(outStream);
6459N/A PrintStream err = NullOutputStream.wrapOrNullStream(errStream);
1795N/A
0N/A BooleanArgument overwriteExisting;
0N/A BooleanArgument showUsage;
6064N/A BooleanArgument useCompareResultCode;
779N/A BooleanArgument singleValueChanges;
4361N/A BooleanArgument doCheckSchema;
0N/A StringArgument configClass;
0N/A StringArgument configFile;
0N/A StringArgument outputLDIF;
0N/A StringArgument sourceLDIF;
0N/A StringArgument targetLDIF;
3758N/A StringArgument ignoreAttrsFile;
3758N/A StringArgument ignoreEntriesFile;
0N/A
0N/A
2086N/A Message toolDescription = INFO_LDIFDIFF_TOOL_DESCRIPTION.get();
536N/A ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
536N/A false);
0N/A try
0N/A {
2086N/A sourceLDIF = new StringArgument(
2086N/A "sourceldif", 's', "sourceLDIF", true,
3220N/A false, true, INFO_FILE_PLACEHOLDER.get(), null, null,
2086N/A INFO_LDIFDIFF_DESCRIPTION_SOURCE_LDIF.get());
0N/A argParser.addArgument(sourceLDIF);
0N/A
2086N/A targetLDIF = new StringArgument(
2086N/A "targetldif", 't', "targetLDIF", true,
3220N/A false, true, INFO_FILE_PLACEHOLDER.get(), null, null,
2086N/A INFO_LDIFDIFF_DESCRIPTION_TARGET_LDIF.get());
0N/A argParser.addArgument(targetLDIF);
0N/A
2086N/A outputLDIF = new StringArgument(
2086N/A "outputldif", 'o', "outputLDIF", false,
3220N/A false, true, INFO_FILE_PLACEHOLDER.get(), null, null,
2086N/A INFO_LDIFDIFF_DESCRIPTION_OUTPUT_LDIF.get());
0N/A argParser.addArgument(outputLDIF);
0N/A
3758N/A ignoreAttrsFile = new StringArgument(
3758N/A "ignoreattrs", 'a', "ignoreAttrs", false,
3758N/A false, true, INFO_FILE_PLACEHOLDER.get(), null, null,
3758N/A INFO_LDIFDIFF_DESCRIPTION_IGNORE_ATTRS.get());
3758N/A argParser.addArgument(ignoreAttrsFile);
3758N/A
3758N/A ignoreEntriesFile = new StringArgument(
3758N/A "ignoreentries", 'e', "ignoreEntries", false,
3758N/A false, true, INFO_FILE_PLACEHOLDER.get(), null, null,
3758N/A INFO_LDIFDIFF_DESCRIPTION_IGNORE_ENTRIES.get());
3758N/A argParser.addArgument(ignoreEntriesFile);
3758N/A
0N/A overwriteExisting =
2086N/A new BooleanArgument(
2086N/A "overwriteexisting", 'O',
2086N/A "overwriteExisting",
2086N/A INFO_LDIFDIFF_DESCRIPTION_OVERWRITE_EXISTING.get());
0N/A argParser.addArgument(overwriteExisting);
0N/A
779N/A singleValueChanges =
2086N/A new BooleanArgument(
2086N/A "singlevaluechanges", 'S', "singleValueChanges",
2086N/A INFO_LDIFDIFF_DESCRIPTION_SINGLE_VALUE_CHANGES.get());
779N/A argParser.addArgument(singleValueChanges);
779N/A
4361N/A doCheckSchema =
4361N/A new BooleanArgument(
4361N/A "checkschema", null, "checkSchema",
4361N/A INFO_LDIFDIFF_DESCRIPTION_CHECK_SCHEMA.get());
4361N/A argParser.addArgument(doCheckSchema);
4361N/A
0N/A configFile = new StringArgument("configfile", 'c', "configFile", false,
3220N/A false, true,
3220N/A INFO_CONFIGFILE_PLACEHOLDER.get(), null,
3220N/A null,
2086N/A INFO_DESCRIPTION_CONFIG_FILE.get());
534N/A configFile.setHidden(true);
0N/A argParser.addArgument(configFile);
0N/A
1060N/A configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
1060N/A OPTION_LONG_CONFIG_CLASS, false,
3220N/A false, true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
0N/A ConfigFileHandler.class.getName(), null,
2086N/A INFO_DESCRIPTION_CONFIG_CLASS.get());
534N/A configClass.setHidden(true);
0N/A argParser.addArgument(configClass);
0N/A
1060N/A showUsage = new BooleanArgument("showusage", OPTION_SHORT_HELP,
1060N/A OPTION_LONG_HELP,
2086N/A INFO_DESCRIPTION_USAGE.get());
0N/A argParser.addArgument(showUsage);
6064N/A
6064N/A useCompareResultCode =
6064N/A new BooleanArgument("usecompareresultcode", 'r',
6071N/A "useCompareResultCode",
6071N/A INFO_LDIFDIFF_DESCRIPTION_USE_COMPARE_RESULT.get());
6064N/A argParser.addArgument(useCompareResultCode);
6064N/A
0N/A argParser.setUsageArgument(showUsage);
0N/A }
0N/A catch (ArgumentException ae)
0N/A {
2086N/A Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A
0N/A
0N/A // Parse the command-line arguments provided to the program.
0N/A try
0N/A {
0N/A argParser.parseArguments(args);
0N/A }
0N/A catch (ArgumentException ae)
0N/A {
2086N/A Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
1795N/A err.println(message);
1795N/A err.println(argParser.getUsage());
6059N/A return CLIENT_SIDE_PARAM_ERROR;
0N/A }
0N/A
0N/A
1340N/A // If we should just display usage or version information,
1340N/A // then print it and exit.
1340N/A if (argParser.usageOrVersionDisplayed())
0N/A {
6059N/A return SUCCESS;
0N/A }
0N/A
4361N/A if (doCheckSchema.isPresent() && !configFile.isPresent())
4361N/A {
4361N/A String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
4361N/A if (scriptName == null)
4361N/A {
4361N/A scriptName = "ldif-diff";
4361N/A }
4361N/A Message message = WARN_LDIFDIFF_NO_CONFIG_FILE.get(scriptName);
4361N/A err.println(message);
4361N/A }
0N/A
4361N/A
4361N/A boolean checkSchema = configFile.isPresent() && doCheckSchema.isPresent();
781N/A if (! serverInitialized)
781N/A {
781N/A // Bootstrap the Directory Server configuration for use as a client.
781N/A DirectoryServer directoryServer = DirectoryServer.getInstance();
3853N/A DirectoryServer.bootstrapClient();
0N/A
0N/A
781N/A // If we're to use the configuration then initialize it, along with the
781N/A // schema.
781N/A if (checkSchema)
0N/A {
781N/A try
781N/A {
3853N/A DirectoryServer.initializeJMX();
781N/A }
781N/A catch (Exception e)
781N/A {
2086N/A
2086N/A Message message = ERR_LDIFDIFF_CANNOT_INITIALIZE_JMX.get(
2086N/A String.valueOf(configFile.getValue()),
781N/A e.getMessage());
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
781N/A }
0N/A
781N/A try
781N/A {
781N/A directoryServer.initializeConfiguration(configClass.getValue(),
781N/A configFile.getValue());
781N/A }
781N/A catch (Exception e)
781N/A {
2086N/A Message message = ERR_LDIFDIFF_CANNOT_INITIALIZE_CONFIG.get(
2086N/A String.valueOf(configFile.getValue()),
781N/A e.getMessage());
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
781N/A }
781N/A
781N/A try
781N/A {
781N/A directoryServer.initializeSchema();
781N/A }
781N/A catch (Exception e)
781N/A {
2086N/A Message message = ERR_LDIFDIFF_CANNOT_INITIALIZE_SCHEMA.get(
2086N/A String.valueOf(configFile.getValue()),
2086N/A e.getMessage());
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
781N/A }
0N/A }
0N/A }
0N/A
3758N/A // Read in ignored entries and attributes if any
3758N/A BufferedReader ignReader = null;
3758N/A Collection<DN> ignoreEntries = new HashSet<DN>();
3758N/A Collection<String> ignoreAttrs = new HashSet<String>();
3758N/A
3758N/A if (ignoreAttrsFile.getValue() != null)
3758N/A {
3758N/A try
3758N/A {
3758N/A ignReader = new BufferedReader(
3758N/A new FileReader(ignoreAttrsFile.getValue()));
3758N/A String line = null;
3758N/A while ((line = ignReader.readLine()) != null)
3758N/A {
3758N/A ignoreAttrs.add(line.toLowerCase());
3758N/A }
3758N/A ignReader.close();
3758N/A }
3758N/A catch (Exception e)
3758N/A {
3758N/A Message message = ERR_LDIFDIFF_CANNOT_READ_FILE_IGNORE_ATTRIBS.get(
3758N/A ignoreAttrsFile.getValue(),
3758N/A String.valueOf(e));
3758N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
3758N/A }
3758N/A finally
3758N/A {
3758N/A try
3758N/A {
3758N/A ignReader.close();
3758N/A }
3758N/A catch (Exception e) {}
3758N/A }
3758N/A }
3758N/A
3758N/A if (ignoreEntriesFile.getValue() != null)
3758N/A {
3758N/A try
3758N/A {
3758N/A ignReader = new BufferedReader(
3758N/A new FileReader(ignoreEntriesFile.getValue()));
3758N/A String line = null;
3758N/A while ((line = ignReader.readLine()) != null)
3758N/A {
3758N/A try
3758N/A {
3758N/A DN dn = DN.decode(line);
3758N/A ignoreEntries.add(dn);
3758N/A }
3758N/A catch (DirectoryException e)
3758N/A {
3758N/A Message message = INFO_LDIFDIFF_CANNOT_PARSE_STRING_AS_DN.get(
3758N/A line, ignoreEntriesFile.getValue());
3758N/A err.println(message);
3758N/A }
3758N/A }
3758N/A ignReader.close();
3758N/A }
3758N/A catch (Exception e)
3758N/A {
3758N/A Message message = ERR_LDIFDIFF_CANNOT_READ_FILE_IGNORE_ENTRIES.get(
3758N/A ignoreEntriesFile.getValue(),
3758N/A String.valueOf(e));
3758N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
3758N/A }
3758N/A finally
3758N/A {
3758N/A try
3758N/A {
3758N/A ignReader.close();
3758N/A }
3758N/A catch (Exception e) {}
3758N/A }
3758N/A }
0N/A
0N/A // Open the source LDIF file and read it into a tree map.
0N/A LDIFReader reader;
0N/A LDIFImportConfig importConfig = new LDIFImportConfig(sourceLDIF.getValue());
0N/A try
0N/A {
0N/A reader = new LDIFReader(importConfig);
0N/A }
0N/A catch (Exception e)
0N/A {
2086N/A Message message = ERR_LDIFDIFF_CANNOT_OPEN_SOURCE_LDIF.get(
2086N/A sourceLDIF.getValue(),
2086N/A String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A
509N/A TreeMap<DN,Entry> sourceMap = new TreeMap<DN,Entry>();
0N/A try
0N/A {
0N/A while (true)
0N/A {
0N/A Entry entry = reader.readEntry(checkSchema);
0N/A if (entry == null)
0N/A {
0N/A break;
0N/A }
0N/A
3758N/A if (! ignoreEntries.contains(entry.getDN()))
3758N/A {
3758N/A sourceMap.put(entry.getDN(), entry);
3758N/A }
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
2086N/A Message message = ERR_LDIFDIFF_ERROR_READING_SOURCE_LDIF.get(
2086N/A sourceLDIF.getValue(),
2086N/A String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A finally
0N/A {
0N/A try
0N/A {
0N/A reader.close();
0N/A } catch (Exception e) {}
0N/A }
0N/A
0N/A
0N/A // Open the target LDIF file and read it into a tree map.
0N/A importConfig = new LDIFImportConfig(targetLDIF.getValue());
0N/A try
0N/A {
0N/A reader = new LDIFReader(importConfig);
0N/A }
0N/A catch (Exception e)
0N/A {
2086N/A Message message = ERR_LDIFDIFF_CANNOT_OPEN_TARGET_LDIF.get(
2086N/A targetLDIF.getValue(),
2086N/A String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A
509N/A TreeMap<DN,Entry> targetMap = new TreeMap<DN,Entry>();
0N/A try
0N/A {
0N/A while (true)
0N/A {
0N/A Entry entry = reader.readEntry(checkSchema);
0N/A if (entry == null)
0N/A {
0N/A break;
0N/A }
0N/A
3758N/A if (! ignoreEntries.contains(entry.getDN()))
3758N/A {
3758N/A targetMap.put(entry.getDN(), entry);
3758N/A }
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
2086N/A Message message = ERR_LDIFDIFF_ERROR_READING_TARGET_LDIF.get(
2086N/A targetLDIF.getValue(),
2086N/A String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A finally
0N/A {
0N/A try
0N/A {
0N/A reader.close();
0N/A } catch (Exception e) {}
0N/A }
0N/A
0N/A
0N/A // Open the output writer that we'll use to write the differences.
0N/A LDIFWriter writer;
0N/A try
0N/A {
0N/A LDIFExportConfig exportConfig;
0N/A if (outputLDIF.isPresent())
0N/A {
0N/A if (overwriteExisting.isPresent())
0N/A {
0N/A exportConfig = new LDIFExportConfig(outputLDIF.getValue(),
0N/A ExistingFileBehavior.OVERWRITE);
0N/A }
0N/A else
0N/A {
0N/A exportConfig = new LDIFExportConfig(outputLDIF.getValue(),
0N/A ExistingFileBehavior.APPEND);
0N/A }
0N/A }
0N/A else
0N/A {
1795N/A exportConfig = new LDIFExportConfig(out);
0N/A }
0N/A
0N/A writer = new LDIFWriter(exportConfig);
0N/A }
0N/A catch (Exception e)
0N/A {
2086N/A Message message = ERR_LDIFDIFF_CANNOT_OPEN_OUTPUT.get(String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A
0N/A
0N/A try
0N/A {
6064N/A boolean differenceFound;
6064N/A
0N/A // Check to see if either or both of the source and target maps are empty.
0N/A if (sourceMap.isEmpty())
0N/A {
0N/A if (targetMap.isEmpty())
0N/A {
0N/A // They're both empty, so there are no differences.
6064N/A differenceFound = false;
0N/A }
0N/A else
0N/A {
0N/A // The target isn't empty, so they're all adds.
0N/A Iterator<DN> targetIterator = targetMap.keySet().iterator();
0N/A while (targetIterator.hasNext())
0N/A {
0N/A writeAdd(writer, targetMap.get(targetIterator.next()));
0N/A }
6064N/A differenceFound = true;
0N/A }
0N/A }
0N/A else if (targetMap.isEmpty())
0N/A {
0N/A // The source isn't empty, so they're all deletes.
0N/A Iterator<DN> sourceIterator = sourceMap.keySet().iterator();
0N/A while (sourceIterator.hasNext())
0N/A {
781N/A writeDelete(writer, sourceMap.get(sourceIterator.next()));
0N/A }
6064N/A differenceFound = true;
0N/A }
0N/A else
0N/A {
6064N/A differenceFound = false;
0N/A // Iterate through all the entries in the source and target maps and
0N/A // identify the differences.
0N/A Iterator<DN> sourceIterator = sourceMap.keySet().iterator();
0N/A Iterator<DN> targetIterator = targetMap.keySet().iterator();
0N/A DN sourceDN = sourceIterator.next();
0N/A DN targetDN = targetIterator.next();
0N/A Entry sourceEntry = sourceMap.get(sourceDN);
0N/A Entry targetEntry = targetMap.get(targetDN);
0N/A
0N/A while (true)
0N/A {
509N/A // Compare the DNs to determine the relative order of the
0N/A // entries.
509N/A int comparatorValue = sourceDN.compareTo(targetDN);
0N/A if (comparatorValue < 0)
0N/A {
0N/A // The source entry should be before the target entry, which means
0N/A // that the source entry has been deleted.
0N/A writeDelete(writer, sourceEntry);
0N/A differenceFound = true;
0N/A if (sourceIterator.hasNext())
0N/A {
0N/A sourceDN = sourceIterator.next();
0N/A sourceEntry = sourceMap.get(sourceDN);
0N/A }
0N/A else
0N/A {
781N/A // There are no more source entries, so if there are more target
781N/A // entries then they're all adds.
781N/A writeAdd(writer, targetEntry);
781N/A
781N/A while (targetIterator.hasNext())
781N/A {
781N/A targetDN = targetIterator.next();
781N/A targetEntry = targetMap.get(targetDN);
781N/A writeAdd(writer, targetEntry);
781N/A differenceFound = true;
781N/A }
781N/A
0N/A break;
0N/A }
0N/A }
0N/A else if (comparatorValue > 0)
0N/A {
0N/A // The target entry should be before the source entry, which means
0N/A // that the target entry has been added.
778N/A writeAdd(writer, targetEntry);
0N/A differenceFound = true;
0N/A if (targetIterator.hasNext())
0N/A {
0N/A targetDN = targetIterator.next();
0N/A targetEntry = targetMap.get(targetDN);
0N/A }
0N/A else
0N/A {
781N/A // There are no more target entries so all of the remaining source
781N/A // entries are deletes.
781N/A writeDelete(writer, sourceEntry);
781N/A differenceFound = true;
781N/A while (sourceIterator.hasNext())
781N/A {
781N/A sourceDN = sourceIterator.next();
781N/A sourceEntry = sourceMap.get(sourceDN);
781N/A writeDelete(writer, sourceEntry);
781N/A }
781N/A
0N/A break;
0N/A }
0N/A }
0N/A else
0N/A {
0N/A // The DNs are the same, so check to see if the entries are the
0N/A // same or have been modified.
3758N/A if (writeModify(writer, sourceEntry, targetEntry, ignoreAttrs,
779N/A singleValueChanges.isPresent()))
0N/A {
0N/A differenceFound = true;
0N/A }
0N/A
0N/A if (sourceIterator.hasNext())
0N/A {
0N/A sourceDN = sourceIterator.next();
0N/A sourceEntry = sourceMap.get(sourceDN);
0N/A }
0N/A else
0N/A {
0N/A // There are no more source entries, so if there are more target
0N/A // entries then they're all adds.
0N/A while (targetIterator.hasNext())
0N/A {
0N/A targetDN = targetIterator.next();
0N/A targetEntry = targetMap.get(targetDN);
0N/A writeAdd(writer, targetEntry);
0N/A differenceFound = true;
0N/A }
0N/A
0N/A break;
0N/A }
0N/A
0N/A if (targetIterator.hasNext())
0N/A {
0N/A targetDN = targetIterator.next();
0N/A targetEntry = targetMap.get(targetDN);
0N/A }
0N/A else
0N/A {
0N/A // There are no more target entries so all of the remaining source
0N/A // entries are deletes.
478N/A writeDelete(writer, sourceEntry);
478N/A differenceFound = true;
478N/A while (sourceIterator.hasNext())
0N/A {
478N/A sourceDN = sourceIterator.next();
478N/A sourceEntry = sourceMap.get(sourceDN);
0N/A writeDelete(writer, sourceEntry);
0N/A }
778N/A
778N/A break;
0N/A }
0N/A }
0N/A }
6064N/A }
0N/A
6064N/A if (!differenceFound)
6064N/A {
6064N/A Message message = INFO_LDIFDIFF_NO_DIFFERENCES.get();
6064N/A writer.writeComment(message, 0);
6064N/A }
6064N/A if (useCompareResultCode.isPresent())
6064N/A {
6064N/A return !differenceFound ? COMPARE_TRUE : COMPARE_FALSE;
0N/A }
0N/A }
0N/A catch (IOException e)
0N/A {
2086N/A Message message =
2086N/A ERR_LDIFDIFF_ERROR_WRITING_OUTPUT.get(String.valueOf(e));
1795N/A err.println(message);
6059N/A return OPERATIONS_ERROR;
0N/A }
0N/A finally
0N/A {
0N/A try
0N/A {
0N/A writer.close();
0N/A } catch (Exception e) {}
0N/A }
0N/A
0N/A
0N/A // If we've gotten to this point, then everything was successful.
6059N/A return SUCCESS;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Writes an add change record to the LDIF writer.
0N/A *
0N/A * @param writer The writer to which the add record should be written.
0N/A * @param entry The entry that has been added.
0N/A *
0N/A * @throws IOException If a problem occurs while attempting to write the add
0N/A * record.
0N/A */
0N/A private static void writeAdd(LDIFWriter writer, Entry entry)
0N/A throws IOException
0N/A {
0N/A writer.writeAddChangeRecord(entry);
0N/A writer.flush();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Writes a delete change record to the LDIF writer, including a comment
0N/A * with the contents of the deleted entry.
0N/A *
0N/A * @param writer The writer to which the delete record should be written.
0N/A * @param entry The entry that has been deleted.
0N/A *
0N/A * @throws IOException If a problem occurs while attempting to write the
0N/A * delete record.
0N/A */
0N/A private static void writeDelete(LDIFWriter writer, Entry entry)
0N/A throws IOException
0N/A {
0N/A writer.writeDeleteChangeRecord(entry, true);
0N/A writer.flush();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Writes a modify change record to the LDIF writer. Note that this will
0N/A * handle all the necessary logic for determining if the entries are actually
0N/A * different, and if they are the same then no output will be generated. Also
0N/A * note that this will only look at differences between the objectclasses and
0N/A * user attributes. It will ignore differences in the DN and operational
0N/A * attributes.
0N/A *
779N/A * @param writer The writer to which the modify record should be
779N/A * written.
779N/A * @param sourceEntry The source form of the entry.
779N/A * @param targetEntry The target form of the entry.
3758N/A * @param ignoreAttrs Attributes that are ignored while calculating
3758N/A * the differences.
779N/A * @param singleValueChanges Indicates whether each attribute-level change
779N/A * should be written in a separate modification
779N/A * per attribute value.
0N/A *
0N/A * @return <CODE>true</CODE> if there were any differences found between the
0N/A * source and target entries, or <CODE>false</CODE> if not.
0N/A *
0N/A * @throws IOException If a problem occurs while attempting to write the
0N/A * change record.
0N/A */
0N/A private static boolean writeModify(LDIFWriter writer, Entry sourceEntry,
3758N/A Entry targetEntry, Collection ignoreAttrs,
779N/A boolean singleValueChanges)
0N/A throws IOException
0N/A {
0N/A // Create a list to hold the modifications that are found.
0N/A LinkedList<Modification> modifications = new LinkedList<Modification>();
0N/A
0N/A
0N/A // Look at the set of objectclasses for the entries.
0N/A LinkedHashSet<ObjectClass> sourceClasses =
0N/A new LinkedHashSet<ObjectClass>(
0N/A sourceEntry.getObjectClasses().keySet());
0N/A LinkedHashSet<ObjectClass> targetClasses =
0N/A new LinkedHashSet<ObjectClass>(
0N/A targetEntry.getObjectClasses().keySet());
0N/A Iterator<ObjectClass> sourceClassIterator = sourceClasses.iterator();
0N/A while (sourceClassIterator.hasNext())
0N/A {
0N/A ObjectClass sourceClass = sourceClassIterator.next();
0N/A if (targetClasses.remove(sourceClass))
0N/A {
0N/A sourceClassIterator.remove();
0N/A }
0N/A }
0N/A
3853N/A if (!sourceClasses.isEmpty())
0N/A {
0N/A // Whatever is left must have been deleted.
0N/A AttributeType attrType = DirectoryServer.getObjectClassAttributeType();
3853N/A AttributeBuilder builder = new AttributeBuilder(attrType);
0N/A for (ObjectClass oc : sourceClasses)
0N/A {
4134N/A builder.add(AttributeValues.create(attrType, oc.getNameOrOID()));
0N/A }
0N/A
3853N/A modifications.add(new Modification(ModificationType.DELETE, builder
3853N/A .toAttribute()));
0N/A }
0N/A
0N/A if (! targetClasses.isEmpty())
0N/A {
0N/A // Whatever is left must have been added.
0N/A AttributeType attrType = DirectoryServer.getObjectClassAttributeType();
3853N/A AttributeBuilder builder = new AttributeBuilder(attrType);
0N/A for (ObjectClass oc : targetClasses)
0N/A {
4134N/A builder.add(AttributeValues.create(attrType, oc.getNameOrOID()));
0N/A }
0N/A
3853N/A modifications.add(new Modification(ModificationType.ADD, builder
3853N/A .toAttribute()));
0N/A }
0N/A
0N/A
0N/A // Look at the user attributes for the entries.
0N/A LinkedHashSet<AttributeType> sourceTypes =
0N/A new LinkedHashSet<AttributeType>(
0N/A sourceEntry.getUserAttributes().keySet());
0N/A Iterator<AttributeType> sourceTypeIterator = sourceTypes.iterator();
0N/A while (sourceTypeIterator.hasNext())
0N/A {
0N/A AttributeType type = sourceTypeIterator.next();
0N/A List<Attribute> sourceAttrs = sourceEntry.getUserAttribute(type);
0N/A List<Attribute> targetAttrs = targetEntry.getUserAttribute(type);
0N/A sourceEntry.removeAttribute(type);
0N/A
0N/A if (targetAttrs == null)
0N/A {
0N/A // The target entry doesn't have this attribute type, so it must have
0N/A // been deleted. In order to make the delete reversible, delete each
0N/A // value individually.
0N/A for (Attribute a : sourceAttrs)
0N/A {
0N/A modifications.add(new Modification(ModificationType.DELETE, a));
0N/A }
0N/A }
0N/A else
0N/A {
0N/A // Check the attributes for differences. We'll ignore differences in
0N/A // the order of the values since that isn't significant.
0N/A targetEntry.removeAttribute(type);
0N/A
0N/A for (Attribute sourceAttr : sourceAttrs)
0N/A {
0N/A Attribute targetAttr = null;
0N/A Iterator<Attribute> attrIterator = targetAttrs.iterator();
0N/A while (attrIterator.hasNext())
0N/A {
0N/A Attribute a = attrIterator.next();
0N/A if (a.optionsEqual(sourceAttr.getOptions()))
0N/A {
0N/A targetAttr = a;
0N/A attrIterator.remove();
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (targetAttr == null)
0N/A {
0N/A // The attribute doesn't exist in the target list, so it has been
0N/A // deleted.
0N/A modifications.add(new Modification(ModificationType.DELETE,
0N/A sourceAttr));
0N/A }
0N/A else
0N/A {
3853N/A // Compare the values.
3853N/A AttributeBuilder addedValuesBuilder = new AttributeBuilder(
3853N/A targetAttr);
3853N/A addedValuesBuilder.removeAll(sourceAttr);
3853N/A Attribute addedValues = addedValuesBuilder.toAttribute();
3853N/A if (!addedValues.isEmpty())
0N/A {
3853N/A modifications.add(new Modification(ModificationType.ADD,
3853N/A addedValues));
0N/A }
0N/A
3853N/A AttributeBuilder deletedValuesBuilder = new AttributeBuilder(
3853N/A sourceAttr);
3853N/A deletedValuesBuilder.removeAll(targetAttr);
3853N/A Attribute deletedValues = deletedValuesBuilder.toAttribute();
3853N/A if (!deletedValues.isEmpty())
0N/A {
0N/A modifications.add(new Modification(ModificationType.DELETE,
3853N/A deletedValues));
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A // Any remaining target attributes have been added.
0N/A for (Attribute targetAttr: targetAttrs)
0N/A {
0N/A modifications.add(new Modification(ModificationType.ADD, targetAttr));
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Any remaining target attribute types have been added.
0N/A for (AttributeType type : targetEntry.getUserAttributes().keySet())
0N/A {
0N/A List<Attribute> targetAttrs = targetEntry.getUserAttribute(type);
0N/A for (Attribute a : targetAttrs)
0N/A {
0N/A modifications.add(new Modification(ModificationType.ADD, a));
0N/A }
0N/A }
0N/A
3758N/A // Remove ignored attributes
3758N/A if (! ignoreAttrs.isEmpty())
3758N/A {
3758N/A ListIterator<Modification> modIter = modifications.listIterator();
3758N/A while (modIter.hasNext())
3758N/A {
3758N/A String name = modIter.next().getAttribute().getName().toLowerCase();
3758N/A if (ignoreAttrs.contains(name))
3758N/A {
3758N/A modIter.remove();
3758N/A }
3758N/A }
3758N/A }
0N/A
0N/A // Write the modification change record.
0N/A if (modifications.isEmpty())
0N/A {
0N/A return false;
0N/A }
0N/A else
0N/A {
779N/A if (singleValueChanges)
779N/A {
779N/A for (Modification m : modifications)
779N/A {
779N/A Attribute a = m.getAttribute();
3853N/A if (a.isEmpty())
779N/A {
779N/A LinkedList<Modification> attrMods = new LinkedList<Modification>();
779N/A attrMods.add(m);
779N/A writer.writeModifyChangeRecord(sourceEntry.getDN(), attrMods);
779N/A }
779N/A else
779N/A {
779N/A LinkedList<Modification> attrMods = new LinkedList<Modification>();
3853N/A for (AttributeValue v : a)
779N/A {
3853N/A AttributeBuilder builder = new AttributeBuilder(a, true);
3853N/A builder.add(v);
3853N/A Attribute attr = builder.toAttribute();
779N/A
779N/A attrMods.clear();
779N/A attrMods.add(new Modification(m.getModificationType(), attr));
779N/A writer.writeModifyChangeRecord(sourceEntry.getDN(), attrMods);
779N/A }
779N/A }
779N/A }
779N/A }
779N/A else
779N/A {
779N/A writer.writeModifyChangeRecord(sourceEntry.getDN(), modifications);
779N/A }
779N/A
0N/A return true;
0N/A }
0N/A }
0N/A}