From f37f462c314da4e3e05ee893e285b8055d923f74 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 07 Sep 2006 19:35:27 +0000
Subject: [PATCH] Add a new ldifmodify tool that works like ldapmodify except that it alters data in an LDIF file rather than over LDAP. There are some limitations, including:
---
opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java | 40 +
opendj-sdk/opends/resource/bin/ldifmodify.sh | 74 ++
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java | 108 +++
opendj-sdk/opends/src/server/org/opends/server/config/ConfigFileHandler.java | 128 ++++
opendj-sdk/opends/resource/bin/ldifmodify.bat | 54 +
opendj-sdk/opends/src/server/org/opends/server/types/Entry.java | 241 ++++++++
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java | 9
opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java | 326 +++++++++++
opendj-sdk/opends/src/server/org/opends/server/tools/LDIFModify.java | 654 +++++++++++++++++++++++
9 files changed, 1,633 insertions(+), 1 deletions(-)
diff --git a/opendj-sdk/opends/resource/bin/ldifmodify.bat b/opendj-sdk/opends/resource/bin/ldifmodify.bat
new file mode 100755
index 0000000..2f20be5
--- /dev/null
+++ b/opendj-sdk/opends/resource/bin/ldifmodify.bat
@@ -0,0 +1,54 @@
+
+@echo off
+rem CDDL HEADER START
+rem
+rem The contents of this file are subject to the terms of the
+rem Common Development and Distribution License, Version 1.0 only
+rem (the "License"). You may not use this file except in compliance
+rem with the License.
+rem
+rem You can obtain a copy of the license at
+rem trunk/opends/resource/legal-notices/OpenDS.LICENSE
+rem or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+rem See the License for the specific language governing permissions
+rem and limitations under the License.
+rem
+rem When distributing Covered Code, include this CDDL HEADER in each
+rem file and include the License file at
+rem trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+rem add the following below this CDDL HEADER, with the fields enclosed
+rem by brackets "[]" replaced with your own identifying * information:
+rem Portions Copyright [yyyy] [name of copyright owner]
+rem
+rem CDDL HEADER END
+rem
+rem
+rem Portions Copyright 2006 Sun Microsystems, Inc.
+
+setlocal
+
+set DIR_HOME=%~dP0..
+
+if "%JAVA_BIN%" == "" goto noJavaBin
+goto setClassPath
+
+:noJavaBin
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+set JAVA_BIN="%JAVA_HOME%\bin\java.exe"
+goto setClassPath
+
+:noJavaHome
+echo Error: JAVA_HOME environment variable is not set.
+echo Please set it to a valid Java 5 installation.
+goto end
+
+
+:setClassPath
+FOR %%x in (%DIR_HOME%\lib\*.jar) DO call "%DIR_HOME%\bin\setcp.bat" %%x
+
+%JAVA_BIN% %JAVA_ARGS% -classpath "%CLASSPATH%" org.opends.server.tools.LDIFModify -c %DIR_HOME%\config\config.ldif %*
+
+
+:end
+
diff --git a/opendj-sdk/opends/resource/bin/ldifmodify.sh b/opendj-sdk/opends/resource/bin/ldifmodify.sh
new file mode 100755
index 0000000..e464bbc
--- /dev/null
+++ b/opendj-sdk/opends/resource/bin/ldifmodify.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# 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
+# trunk/opends/resource/legal-notices/OpenDS.LICENSE
+# or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+# 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
+# trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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
+#
+#
+# Portions Copyright 2006 Sun Microsystems, Inc.
+
+
+# Capture the current working directory so that we can change to it later.
+# Then capture the location of this script and the Directory Server instance
+# root so that we can use them to create appropriate paths.
+WORKING_DIR=`pwd`
+
+cd `dirname $0`
+SCRIPT_DIR=`pwd`
+
+cd ..
+INSTANCE_ROOT=`pwd`
+export INSTANCE_ROOT
+
+cd ${WORKING_DIR}
+
+
+# See if JAVA_HOME is set. If not, then see if there is a java executable in
+# the path and try to figure it out.
+if test -z "${JAVA_BIN}"
+then
+ if test -z "${JAVA_HOME}"
+ then
+ JAVA_BIN=`which java 2> /dev/null`
+ if test $? -eq 0
+ then
+ export JAVA_BIN
+ else
+ echo "Please set JAVA_HOME to the root of a Java 5.0 installation."
+ exit 1
+ fi
+ else
+ JAVA_BIN=${JAVA_HOME}/bin/java
+ export JAVA_BIN
+ fi
+fi
+
+CLASSPATH=${INSTANCE_ROOT}/classes
+for JAR in ${INSTANCE_ROOT}/lib/*.jar
+do
+ CLASSPATH=${CLASSPATH}:${JAR}
+done
+export CLASSPATH
+
+
+${JAVA_BIN} ${JAVA_ARGS} org.opends.server.tools.LDIFModify \
+ -c ${INSTANCE_ROOT}/config/config.ldif "$@"
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 12875ff..44e2c25 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2417,6 +2417,15 @@
/**
+ * The base name (with no path information) of the file that may contain
+ * changes in LDIF form to apply to the configuration before the configuration
+ * is loaded and initialized.
+ */
+ public static final String CONFIG_CHANGES_NAME = "config-changes.ldif";
+
+
+
+ /**
* The name of the directory that will hold the configuration file for the
* Directory Server.
*/
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigFileHandler.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigFileHandler.java
index 33bea8b..c23021b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigFileHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigFileHandler.java
@@ -32,6 +32,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
+import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
@@ -69,6 +70,7 @@
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
+import org.opends.server.tools.LDIFModify;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.BackupInfo;
@@ -180,10 +182,10 @@
// Make sure that the configuration file exists.
this.configFile = configFile;
+ File f = new File(configFile);
try
{
- File f = new File(configFile);
if (! f.exists())
{
int msgID = MSGID_CONFIG_FILE_DOES_NOT_EXIST;
@@ -210,6 +212,27 @@
// Fixme -- Should we add a hash or signature check here?
+ // See if there is a config changes file. If there is, then try to apply
+ // the changes contained in it.
+ File changesFile = new File(f.getParent(), CONFIG_CHANGES_NAME);
+ try
+ {
+ if (changesFile.exists())
+ {
+ applyChangesFile(f, changesFile);
+ }
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeConfigHandler", e);
+
+ int msgID = MSGID_CONFIG_UNABLE_TO_APPLY_STARTUP_CHANGES;
+ String message = getMessage(msgID, changesFile.getAbsolutePath(),
+ String.valueOf(e));
+ throw new InitializationException(msgID, message, e);
+ }
+
+
// We will use the LDIF reader to read the configuration file. Create an
// LDIF import configuration to do this and then get the reader.
LDIFReader reader;
@@ -588,6 +611,109 @@
/**
+ * Applies the updates in the provided changes file to the content in the
+ * specified source file. The result will be written to a temporary file, the
+ * current source file will be moved out of place, and then the updated file
+ * will be moved into the place of the original file. The changes file will
+ * also be renamed so it won't be applied again.
+ * <BR><BR>
+ * If any problems are encountered, then the config initialization process
+ * will be aborted.
+ *
+ * @param sourceFile The LDIF file containing the source data.
+ * @param changesFile The LDIF file containing the changes to apply.
+ *
+ * @throws IOException If a problem occurs while performing disk I/O.
+ *
+ * @throws LDIFException If a problem occurs while trying to interpret the
+ * data.
+ */
+ private void applyChangesFile(File sourceFile, File changesFile)
+ throws IOException, LDIFException
+ {
+ assert debugEnter(CLASS_NAME, "applyChangesFile",
+ String.valueOf(sourceFile), String.valueOf(changesFile));
+
+
+ // FIXME -- Do we need to do anything special for configuration archiving?
+
+
+ // Create the appropriate LDIF readers and writer.
+ LDIFImportConfig importConfig =
+ new LDIFImportConfig(sourceFile.getAbsolutePath());
+ importConfig.setValidateSchema(false);
+ LDIFReader sourceReader = new LDIFReader(importConfig);
+
+ importConfig = new LDIFImportConfig(changesFile.getAbsolutePath());
+ importConfig.setValidateSchema(false);
+ LDIFReader changesReader = new LDIFReader(importConfig);
+
+ String tempFile = changesFile.getAbsolutePath() + ".tmp";
+ LDIFExportConfig exportConfig =
+ new LDIFExportConfig(tempFile, ExistingFileBehavior.OVERWRITE);
+ LDIFWriter targetWriter = new LDIFWriter(exportConfig);
+
+
+ // Apply the changes and make sure there were no errors.
+ LinkedList<String> errorList = new LinkedList<String>();
+ boolean successful = LDIFModify.modifyLDIF(sourceReader, changesReader,
+ targetWriter, errorList);
+
+ try
+ {
+ sourceReader.close();
+ } catch (Exception e) {}
+
+ try
+ {
+ changesReader.close();
+ } catch (Exception e) {}
+
+ try
+ {
+ targetWriter.close();
+ } catch (Exception e) {}
+
+ if (! successful)
+ {
+ // FIXME -- Log each error message and throw an exception.
+ for (String s : errorList)
+ {
+ int msgID = MSGID_CONFIG_ERROR_APPLYING_STARTUP_CHANGE;
+ String message = getMessage(msgID, s);
+ logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+ msgID, message);
+ }
+
+ int msgID = MSGID_CONFIG_UNABLE_TO_APPLY_CHANGES_FILE;
+ String message = getMessage(msgID);
+ throw new LDIFException(msgID, message);
+ }
+
+
+ // Move the current config file out of the way and replace it with the
+ // updated version.
+ File oldSource = new File(sourceFile.getAbsolutePath() + ".prechanges");
+ if (oldSource.exists())
+ {
+ oldSource.delete();
+ }
+ sourceFile.renameTo(oldSource);
+ new File(tempFile).renameTo(sourceFile);
+
+
+ // Move the changes file out of the way so it doesn't get applied again.
+ File newChanges = new File(changesFile.getAbsolutePath() + ".applied");
+ if (newChanges.exists())
+ {
+ newChanges.delete();
+ }
+ changesFile.renameTo(newChanges);
+ }
+
+
+
+ /**
* Finalizes this configuration handler so that it will release any resources
* associated with it so that it will no longer be used. This will be called
* when the Directory Server is shutting down, as well as in the startup
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
index fdfb12d..02ec5d7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6094,6 +6094,37 @@
/**
+ * The message ID for the message that will be used an error occurs while
+ * attempting to apply a set of changes on server startup. This takes two
+ * arguments, which are the path to the changes file and a message explaining
+ * the problem that occurred.
+ */
+ public static final int MSGID_CONFIG_UNABLE_TO_APPLY_STARTUP_CHANGES =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_FATAL_ERROR | 563;
+
+
+
+ /**
+ * The message ID for the message that will be used to report an error that
+ * occurred while processing a startup changes file. This takes a single
+ * argument, which is a message explaining the problem that occurred.
+ */
+ public static final int MSGID_CONFIG_ERROR_APPLYING_STARTUP_CHANGE =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_FATAL_ERROR | 564;
+
+
+
+ /**
+ * The message ID for the message that will be used to indicate that a problem
+ * occurred while applying the startup changes. This does not take any
+ * arguments.
+ */
+ public static final int MSGID_CONFIG_UNABLE_TO_APPLY_CHANGES_FILE =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_FATAL_ERROR | 565;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -6260,6 +6291,10 @@
registerMessage(MSGID_CONFIG_FILE_CANNOT_VERIFY_EXISTENCE,
"An unexpected error occurred while attempting to " +
"determine whether configuration file %s exists: %s.");
+ registerMessage(MSGID_CONFIG_UNABLE_TO_APPLY_STARTUP_CHANGES,
+ "An error occurred while attempting to apply the changes " +
+ "contained in file %s to the server configuration at " +
+ "startup: %s.");
registerMessage(MSGID_CONFIG_FILE_CANNOT_OPEN_FOR_READ,
"An error occurred while attempting to open the " +
"configuration file %s for reading: %s.");
@@ -6314,6 +6349,11 @@
"An unexpected error occurred while trying to register " +
"the configuration handler base DN \"%s\" as a private " +
"suffix with the Directory Server: %s.");
+ registerMessage(MSGID_CONFIG_ERROR_APPLYING_STARTUP_CHANGE,
+ "Unable to apply a change at server startup: %s.");
+ registerMessage(MSGID_CONFIG_UNABLE_TO_APPLY_CHANGES_FILE,
+ "One or more errors occurred while applying changes on " +
+ "server startup.");
registerMessage(MSGID_CONFIG_FILE_ADD_ALREADY_EXISTS,
"Entry %s cannot be added to the Directory Server " +
"configuration because another configuration entry " +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index f5df4dc..9b600f3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5845,6 +5845,88 @@
/**
+ * The message ID for the message that will be used if an attempt is made to
+ * add an attribute with one or more values that conflict with existing
+ * values. This takes a single argument, which is the name of the attribute.
+ */
+ public static final int MSGID_ENTRY_DUPLICATE_VALUES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 559;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * remove an attribute value that doesn't exist in the entry. This takes a
+ * single argument, which is the name of the attribute.
+ */
+ public static final int MSGID_ENTRY_NO_SUCH_VALUE =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 560;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * perform an increment on the objectClass attribute. This does not take any
+ * arguments.
+ */
+ public static final int MSGID_ENTRY_OC_INCREMENT_NOT_SUPPORTED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 561;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * perform a modify with an unknown modification type. This does not take any
+ * arguments.
+ */
+ public static final int MSGID_ENTRY_UNKNOWN_MODIFICATION_TYPE =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 562;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * increment an attribute that does not exist. This takes a single argument,
+ * which is the name of the attribute.
+ */
+ public static final int MSGID_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 562;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * increment an attribute that has multiple values. This takes a single
+ * argument, which is the name of the attribute.
+ */
+ public static final int MSGID_ENTRY_INCREMENT_MULTIPLE_VALUES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 563;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * perform an increment but there was not exactly one value in the provided
+ * modification. This takes a single argument, which is the name of the
+ * attribute.
+ */
+ public static final int MSGID_ENTRY_INCREMENT_INVALID_VALUE_COUNT =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 564;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * perform an increment but either the current value or the increment cannot
+ * be parsed as an integer. This takes a single argument, which is the name
+ * of the attribute.
+ */
+ public static final int MSGID_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 565;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined
* in this class.
*/
@@ -6307,6 +6389,32 @@
registerMessage(MSGID_ENTRY_ADD_DUPLICATE_OC,
"Objectclass %s is already present in entry %s and " +
"cannot be added a second time.");
+ registerMessage(MSGID_ENTRY_DUPLICATE_VALUES,
+ "Unable to add one or more values to attribute %s " +
+ "because at least one of the values already exists.");
+ registerMessage(MSGID_ENTRY_NO_SUCH_VALUE,
+ "Unable to remove one or more values from attribute %s " +
+ "because at least one of the attributes does not exist " +
+ "in the entry.");
+ registerMessage(MSGID_ENTRY_OC_INCREMENT_NOT_SUPPORTED,
+ "The increment operation is not supported for the " +
+ "objectClass attribute.");
+ registerMessage(MSGID_ENTRY_UNKNOWN_MODIFICATION_TYPE,
+ "Unknown modification type %s requested.");
+ registerMessage(MSGID_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE,
+ "Unable to increment the value of attribute %s because " +
+ "that attribute does not exist in the entry.");
+ registerMessage(MSGID_ENTRY_INCREMENT_MULTIPLE_VALUES,
+ "Unable to increment the value of attribute %s because " +
+ "there are multiple values for that attribute.");
+ registerMessage(MSGID_ENTRY_INCREMENT_INVALID_VALUE_COUNT,
+ "Unable to increment the value of attribute %s because " +
+ "the provided modification did not have exactly one " +
+ "value to use as the increment.");
+ registerMessage(MSGID_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT,
+ "Unable to increment the value of attribute %s because " +
+ "either the current value or the increment could not " +
+ "be parsed as an integer.");
registerMessage(MSGID_SEARCH_FILTER_NULL,
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
index d4335f1..ee87283 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -6003,6 +6003,254 @@
/**
+ * The message ID for the message that will be used if an attempt is made to
+ * add an entry twice in the same set of changes. This takes a single
+ * argument, which is the DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_ADD_ENTRY_TWICE =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 610;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * delete an entry that had just been added in the same set of changes. This
+ * takes a single argument, which is the DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_DELETE_AFTER_ADD =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 611;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * modify an entry that had just been added or deleted in the same set of
+ * changes. This takes a single argument, which is the DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_MODIFY_ADDED_OR_DELETED =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 612;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * perform a modify DN operation. This takes a single argument, which is the
+ * DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_MODDN_NOT_SUPPORTED =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 613;
+
+
+
+ /**
+ * The message ID for the message that will be used if a change record has an
+ * unknown changetype. This takes two arguments, which are the DN of the
+ * entry and the specified changetype.
+ */
+ public static final int MSGID_LDIFMODIFY_UNKNOWN_CHANGETYPE =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 614;
+
+
+
+ /**
+ * The message ID for the message that will be used if an entry to be added
+ * already exists in the data. This takes a single argument, which is the DN
+ * of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_ADD_ALREADY_EXISTS =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 615;
+
+
+
+ /**
+ * The message ID for the message that will be used if an entry cannot be
+ * deleted because it does not exist in the data set. This takes a single
+ * argument, which is the DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_DELETE_NO_SUCH_ENTRY =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 616;
+
+
+
+ /**
+ * The message ID for the message that will be used if an entry cannot be
+ * modified because it does not exist in the data set. This takes a single
+ * argument, which is the DN of the entry.
+ */
+ public static final int MSGID_LDIFMODIFY_MODIFY_NO_SUCH_ENTRY =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 617;
+
+
+ /**
+ * The message ID for the message that will be used as the description of the
+ * configFile argument. This does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_FILE =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 618;
+
+
+ /**
+ * The message ID for the message that will be used as the description of the
+ * configClass argument. This does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_CLASS =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 619;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * sourceLDIF argument. It does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_SOURCE =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 620;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * changesLDIF argument. It does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_CHANGES =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 621;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * targetLDIF argument. It does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_TARGET =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 622;
+
+
+
+ /**
+ * The message ID for the message that will be used as the description for the
+ * help argument. It does not take any arguments.
+ */
+ public static final int MSGID_LDIFMODIFY_DESCRIPTION_HELP =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 623;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to initialize the command-line argument parser. This takes a
+ * single argument, which is a message explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_INITIALIZE_ARGS =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 624;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * parsing the provided command-line arguments. This takes a single argument,
+ * which is a message explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_ERROR_PARSING_ARGS =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 625;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * initializing the Directory Server JMX subsystem. This takes two arguments,
+ * which are the path to the Directory Server configuration file and a message
+ * explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_INITIALIZE_JMX =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 626;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * initializing the Directory Server configuration. This takes two
+ * arguments, which are the path to the Directory Server configuration file
+ * and a message explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_INITIALIZE_CONFIG =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 627;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * initializing the Directory Server schema. This takes two arguments, which
+ * are the path to the Directory Server configuration file and a message
+ * explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_INITIALIZE_SCHEMA =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 628;
+
+
+
+ /**
+ * The message ID for the message that will be used if the source LDIF file
+ * does not exist. This takes a single argument, which is the path to the
+ * source LDIF file.
+ */
+ public static final int MSGID_LDIFMODIFY_SOURCE_DOES_NOT_EXIST =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 629;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to open the source LDIF file. This takes two arguments, which are
+ * the path to the source LDIF file and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_OPEN_SOURCE =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 630;
+
+
+
+ /**
+ * The message ID for the message that will be used if the changes LDIF file
+ * does not exist. This takes a single argument, which is the path to the
+ * changes LDIF file.
+ */
+ public static final int MSGID_LDIFMODIFY_CHANGES_DOES_NOT_EXIST =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 631;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to open the changes LDIF file. This takes two arguments, which are
+ * the path to the changes LDIF file and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_OPEN_CHANGES =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 632;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to open the target LDIF file. This takes two arguments, which are
+ * the path to the target LDIF file and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_CANNOT_OPEN_TARGET =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 633;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * processing the changes. This takes a single argument, which is a message
+ * explaining the problem that occurred.
+ */
+ public static final int MSGID_LDIFMODIFY_ERROR_PROCESSING_LDIF =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 634;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -7977,6 +8225,84 @@
"LDIF: %s.");
registerMessage(MSGID_MAKELDIF_PROCESSING_COMPLETE,
"LDIF processing complete. %d entries written.");
+
+
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_ADD_ENTRY_TWICE,
+ "Entry %s is added twice in the set of changes to apply, " +
+ "which is not supported by the LDIF modify tool.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_DELETE_AFTER_ADD,
+ "Entry %s cannot be deleted because it was previously " +
+ "added in the set of changes. This is not supported by " +
+ "the LDIF modify tool.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_MODIFY_ADDED_OR_DELETED,
+ "Cannot modify entry %s because it was previously added " +
+ "or deleted in the set of changes. This is not " +
+ "supported by the LDIF modify tool.");
+ registerMessage(MSGID_LDIFMODIFY_MODDN_NOT_SUPPORTED,
+ "The modify DN operation targeted at entry %s cannot be " +
+ "processed because modify DN operations are not " +
+ "supported by the LDIF modify tool.");
+ registerMessage(MSGID_LDIFMODIFY_UNKNOWN_CHANGETYPE,
+ "Entry %s has an unknown changetype of %s.");
+ registerMessage(MSGID_LDIFMODIFY_ADD_ALREADY_EXISTS,
+ "Unable to add entry %s because it already exists in " +
+ "the data set.");
+ registerMessage(MSGID_LDIFMODIFY_DELETE_NO_SUCH_ENTRY,
+ "Unable to delete entry %s because it does not exist " +
+ "in the data set.");
+ registerMessage(MSGID_LDIFMODIFY_MODIFY_NO_SUCH_ENTRY,
+ "Unable to modify entry %s because it does not exist " +
+ "in the data set.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_FILE,
+ "The path to the Directory Server configuration file, " +
+ "which will enable the use of the schema definitions " +
+ "when processing the updates. If it is not provided, " +
+ "then schema processing will not be available.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_CLASS,
+ "The fully-qualified name of the Java class to use as " +
+ "the Directory Server configuration handler. If this is " +
+ "not provided, then a default of " +
+ ConfigFileHandler.class.getName() + " will be used.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_SOURCE,
+ "Specifies the LDIF file containing the data to be " +
+ "updated.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_CHANGES,
+ "Specifies he LDIF file containing the changes to apply.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_TARGET,
+ "Specifies he file to which the updated data should be " +
+ "written.");
+ registerMessage(MSGID_LDIFMODIFY_DESCRIPTION_HELP,
+ "Displays this usage information.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_INITIALIZE_ARGS,
+ "An unexpected error occurred while attempting to " +
+ "initialize the command-line arguments: %s.");
+ registerMessage(MSGID_LDIFMODIFY_ERROR_PARSING_ARGS,
+ "An error occurred while parsing the command-line " +
+ "arguments: %s.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_INITIALIZE_JMX,
+ "An error occurred while attempting to initialize the " +
+ "Directory Server JMX subsystem based on the information " +
+ "in configuration file %s: %s.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_INITIALIZE_CONFIG,
+ "An error occurred while attempting to process the " +
+ "Directory Server configuration file %s: %s.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_INITIALIZE_SCHEMA,
+ "An error occurred while attempting to initialize the " +
+ "Directory Server schema based on the information in " +
+ "configuration file %s: %s.");
+ registerMessage(MSGID_LDIFMODIFY_SOURCE_DOES_NOT_EXIST,
+ "The source LDIF file %s does not exist.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_OPEN_SOURCE,
+ "Unable to open the source LDIF file %s: %s.");
+ registerMessage(MSGID_LDIFMODIFY_CHANGES_DOES_NOT_EXIST,
+ "The changes LDIF file %s does not exist.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_OPEN_CHANGES,
+ "Unable to open the changes LDIF file %s: %s.");
+ registerMessage(MSGID_LDIFMODIFY_CANNOT_OPEN_TARGET,
+ "Unable to open the target LDIF file %s for writing: %s.");
+ registerMessage(MSGID_LDIFMODIFY_ERROR_PROCESSING_LDIF,
+ "An error occurred while processing the requested " +
+ "changes: %s.");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDIFModify.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDIFModify.java
new file mode 100644
index 0000000..6c582be
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDIFModify.java
@@ -0,0 +1,654 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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
+ *
+ *
+ * Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.tools;
+
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.opends.server.config.ConfigFileHandler;
+import org.opends.server.core.DirectoryException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.ldap.LDAPException;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ExistingFileBehavior;
+import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ObjectClass;
+import org.opends.server.util.AddChangeRecordEntry;
+import org.opends.server.util.ChangeRecordEntry;
+import org.opends.server.util.DeleteChangeRecordEntry;
+import org.opends.server.util.LDIFException;
+import org.opends.server.util.LDIFReader;
+import org.opends.server.util.LDIFWriter;
+import org.opends.server.util.ModifyChangeRecordEntry;
+import org.opends.server.util.args.ArgumentException;
+import org.opends.server.util.args.ArgumentParser;
+import org.opends.server.util.args.BooleanArgument;
+import org.opends.server.util.args.StringArgument;
+
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.messages.ToolMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class provides a program that may be used to apply a set of changes (in
+ * LDIF change format) to an LDIF file. It will first read all of the changes
+ * into memory, and then will iterate through an LDIF file and apply them to the
+ * entries contained in it. Note that because of the manner in which it
+ * processes the changes, certain types of operations will not be allowed,
+ * including:
+ * <BR>
+ * <UL>
+ * <LI>Modify DN operations</LI>
+ * <LI>Deleting an entry that has been added</LI>
+ * <LI>Modifying an entry that has been added</LI>
+ * </UL>
+ */
+public class LDIFModify
+{
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME = "org.opends.server.tools.LDIFModify";
+
+
+
+ /**
+ * Applies the specified changes to the source LDIF, writing the modified
+ * file to the specified target. Neither the readers nor the writer will be
+ * closed.
+ *
+ * @param sourceReader The LDIF reader that will be used to read the LDIF
+ * content to be modified.
+ * @param changeReader The LDIF reader that will be used to read the changes
+ * to be applied.
+ * @param targetWriter The LDIF writer that will be used to write the
+ * modified LDIF.
+ * @param errorList A list into which any error messages generated while
+ * processing changes may be added.
+ *
+ * @return <CODE>true</CODE> if all updates were successfully applied, or
+ * <CODE>false</CODE> if any errors were encountered.
+ *
+ * @throws IOException If a problem occurs while attempting to read the
+ * source or changes, or write the target.
+ *
+ * @throws LDIFException If a problem occurs while attempting to decode the
+ * source or changes, or trying to determine whether
+ * to include the entry in the output.
+ */
+ public static boolean modifyLDIF(LDIFReader sourceReader,
+ LDIFReader changeReader,
+ LDIFWriter targetWriter,
+ List<String> errorList)
+ throws IOException, LDIFException
+ {
+ // Read the changes into memory.
+ HashMap<DN,AddChangeRecordEntry> adds =
+ new HashMap<DN,AddChangeRecordEntry>();
+ HashMap<DN,DeleteChangeRecordEntry> deletes =
+ new HashMap<DN,DeleteChangeRecordEntry>();
+ HashMap<DN,LinkedList<Modification>> modifications =
+ new HashMap<DN,LinkedList<Modification>>();
+
+ while (true)
+ {
+ ChangeRecordEntry changeRecord;
+ try
+ {
+ changeRecord = changeReader.readChangeRecord(false);
+ }
+ catch (LDIFException le)
+ {
+ if (le.canContinueReading())
+ {
+ errorList.add(le.getMessage());
+ continue;
+ }
+ else
+ {
+ throw le;
+ }
+ }
+
+ if (changeRecord == null)
+ {
+ break;
+ }
+
+ DN changeDN = changeRecord.getDN();
+ switch (changeRecord.getChangeOperationType())
+ {
+ case ADD:
+ // The entry must not exist in the add list.
+ if (adds.containsKey(changeDN))
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_ADD_ENTRY_TWICE;
+ errorList.add(getMessage(msgID, String.valueOf(changeDN)));
+ continue;
+ }
+ else
+ {
+ adds.put(changeDN, (AddChangeRecordEntry) changeRecord);
+ }
+ break;
+
+ case DELETE:
+ // The entry must not exist in the add list. If it exists in the
+ // modify list, then remove the changes since we won't need to apply
+ // them.
+ if (adds.containsKey(changeDN))
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_DELETE_AFTER_ADD;
+ errorList.add(getMessage(msgID, String.valueOf(changeDN)));
+ continue;
+ }
+ else
+ {
+ modifications.remove(changeDN);
+ deletes.put(changeDN, (DeleteChangeRecordEntry) changeRecord);
+ }
+ break;
+
+ case MODIFY:
+ // The entry must not exist in the add or delete lists.
+ if (adds.containsKey(changeDN) || deletes.containsKey(changeDN))
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_MODIFY_ADDED_OR_DELETED;
+ errorList.add(getMessage(msgID, String.valueOf(changeDN)));
+ continue;
+ }
+ else
+ {
+ LinkedList<Modification> mods =
+ modifications.get(changeDN);
+ if (mods == null)
+ {
+ mods = new LinkedList<Modification>();
+ modifications.put(changeDN, mods);
+ }
+
+ for (LDAPModification mod :
+ ((ModifyChangeRecordEntry) changeRecord).getModifications())
+ {
+ try
+ {
+ mods.add(mod.toModification());
+ }
+ catch (LDAPException le)
+ {
+ errorList.add(le.getMessage());
+ continue;
+ }
+ }
+ }
+ break;
+
+ case MODIFY_DN:
+ int msgID = MSGID_LDIFMODIFY_MODDN_NOT_SUPPORTED;
+ errorList.add(getMessage(msgID, String.valueOf(changeDN)));
+ continue;
+
+ default:
+ msgID = MSGID_LDIFMODIFY_UNKNOWN_CHANGETYPE;
+ errorList.add(getMessage(msgID, String.valueOf(changeDN),
+ String.valueOf(changeRecord.getChangeOperationType())));
+ continue;
+ }
+ }
+
+
+ // Read the source an entry at a time and apply any appropriate changes
+ // before writing to the target LDIF.
+ while (true)
+ {
+ Entry entry;
+ try
+ {
+ entry = sourceReader.readEntry();
+ }
+ catch (LDIFException le)
+ {
+ if (le.canContinueReading())
+ {
+ errorList.add(le.getMessage());
+ continue;
+ }
+ else
+ {
+ throw le;
+ }
+ }
+
+ if (entry == null)
+ {
+ break;
+ }
+
+
+ // If the entry is to be deleted, then just skip over it without writing
+ // it to the output.
+ DN entryDN = entry.getDN();
+ if (deletes.remove(entryDN) != null)
+ {
+ continue;
+ }
+
+
+ // If the entry is to be added, then that's an error, since it already
+ // exists.
+ if (adds.remove(entryDN) != null)
+ {
+ int msgID = MSGID_LDIFMODIFY_ADD_ALREADY_EXISTS;
+ errorList.add(getMessage(msgID, String.valueOf(entryDN)));
+ continue;
+ }
+
+
+ // If the entry is to be modified, then process the changes.
+ LinkedList<Modification> mods = modifications.remove(entryDN);
+ if ((mods != null) && (! mods.isEmpty()))
+ {
+ try
+ {
+ entry.applyModifications(mods);
+ }
+ catch (DirectoryException de)
+ {
+ errorList.add(de.getErrorMessage());
+ continue;
+ }
+ }
+
+
+ // If we've gotten here, then the (possibly updated) entry should be
+ // written to the output LDIF.
+ targetWriter.writeEntry(entry);
+ }
+
+
+ // Perform any adds that may be necessary.
+ for (AddChangeRecordEntry add : adds.values())
+ {
+ Map<ObjectClass,String> objectClasses =
+ new LinkedHashMap<ObjectClass,String>();
+ Map<AttributeType,List<Attribute>> userAttributes =
+ new LinkedHashMap<AttributeType,List<Attribute>>();
+ Map<AttributeType,List<Attribute>> operationalAttributes =
+ new LinkedHashMap<AttributeType,List<Attribute>>();
+
+ for (Attribute a : add.getAttributes())
+ {
+ AttributeType t = a.getAttributeType();
+ if (t.isObjectClassType())
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ String stringValue = v.getStringValue();
+ String lowerValue = toLowerCase(stringValue);
+ ObjectClass oc = DirectoryServer.getObjectClass(lowerValue, true);
+ objectClasses.put(oc, stringValue);
+ }
+ }
+ else if (t.isOperational())
+ {
+ List<Attribute> attrList = operationalAttributes.get(t);
+ if (attrList == null)
+ {
+ attrList = new LinkedList<Attribute>();
+ operationalAttributes.put(t, attrList);
+ }
+ attrList.add(a);
+ }
+ else
+ {
+ List<Attribute> attrList = userAttributes.get(t);
+ if (attrList == null)
+ {
+ attrList = new LinkedList<Attribute>();
+ userAttributes.put(t, attrList);
+ }
+ attrList.add(a);
+ }
+ }
+
+ Entry e = new Entry(add.getDN(), objectClasses, userAttributes,
+ operationalAttributes);
+ targetWriter.writeEntry(e);
+ }
+
+
+ // If there are any entries left in the delete or modify lists, then that's
+ // a problem because they didn't exist.
+ if (! deletes.isEmpty())
+ {
+ for (DN dn : deletes.keySet())
+ {
+ int msgID = MSGID_LDIFMODIFY_DELETE_NO_SUCH_ENTRY;
+ errorList.add(getMessage(msgID, String.valueOf(dn)));
+ }
+ }
+
+ if (! modifications.isEmpty())
+ {
+ for (DN dn : modifications.keySet())
+ {
+ int msgID = MSGID_LDIFMODIFY_MODIFY_NO_SUCH_ENTRY;
+ errorList.add(getMessage(msgID, String.valueOf(dn)));
+ }
+ }
+
+ return errorList.isEmpty();
+ }
+
+
+
+ /**
+ * Invokes <CODE>ldifModifyMain</CODE> to perform the appropriate processing.
+ *
+ * @param args The command-line arguments provided to the client.
+ */
+ public static void main(String[] args)
+ {
+ int returnCode = ldifModifyMain(args);
+ if (returnCode != 0)
+ {
+ System.exit(returnCode);
+ }
+ }
+
+
+
+ /**
+ * Processes the command-line arguments and makes the appropriate updates to
+ * the LDIF file.
+ *
+ * @param args The command-line arguments provided to the client.
+ *
+ * @return A value of zero if everything completed properly, or nonzero if
+ * any problem(s) occurred.
+ */
+ public static int ldifModifyMain(String[] args)
+ {
+ // Prepare the argument parser.
+ BooleanArgument showUsage;
+ StringArgument changesFile;
+ StringArgument configClass;
+ StringArgument configFile;
+ StringArgument sourceFile;
+ StringArgument targetFile;
+
+ ArgumentParser argParser = new ArgumentParser(CLASS_NAME, false);
+
+ try
+ {
+ configFile = new StringArgument("configfile", 'c', "configFile", true,
+ false, true, "{configFile}", null, null,
+ MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_FILE);
+ argParser.addArgument(configFile);
+
+
+ configClass = new StringArgument("configclass", 'C', "configClass", false,
+ false, true, "{configClass}",
+ ConfigFileHandler.class.getName(), null,
+ MSGID_LDIFMODIFY_DESCRIPTION_CONFIG_CLASS);
+ argParser.addArgument(configClass);
+
+
+ sourceFile = new StringArgument("sourceldif", 's', "sourceLDIF", true,
+ false, true, "{file}", null, null,
+ MSGID_LDIFMODIFY_DESCRIPTION_SOURCE);
+ argParser.addArgument(sourceFile);
+
+
+ changesFile = new StringArgument("changesldif", 'm', "changesLDIF", true,
+ false, true, "{file}", null, null,
+ MSGID_LDIFMODIFY_DESCRIPTION_CHANGES);
+ argParser.addArgument(changesFile);
+
+
+ targetFile = new StringArgument("targetldif", 't', "targetLDIF", true,
+ false, true, "{file}", null, null,
+ MSGID_LDIFMODIFY_DESCRIPTION_TARGET);
+ argParser.addArgument(targetFile);
+
+
+ showUsage = new BooleanArgument("help", 'H', "help",
+ MSGID_LDIFMODIFY_DESCRIPTION_HELP);
+ argParser.addArgument(showUsage);
+ argParser.setUsageArgument(showUsage);
+ }
+ catch (ArgumentException ae)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_INITIALIZE_ARGS;
+ String message = getMessage(msgID, ae.getMessage());
+ System.err.println(message);
+ return 1;
+ }
+
+
+ // Parse the command-line arguments provided to the program.
+ try
+ {
+ argParser.parseArguments(args);
+ }
+ catch (ArgumentException ae)
+ {
+ int msgID = MSGID_LDIFMODIFY_ERROR_PARSING_ARGS;
+ String message = getMessage(msgID, ae.getMessage());
+
+ System.err.println(message);
+ System.err.println(argParser.getUsage());
+ return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
+ }
+
+
+ // If we should just display usage information, then print it and exit.
+ if (showUsage.isPresent())
+ {
+ return 0;
+ }
+
+
+ // Bootstrap the Directory Server configuration for use as a client.
+ DirectoryServer directoryServer = DirectoryServer.getInstance();
+ directoryServer.bootstrapClient();
+
+
+ // If we're to use the configuration then initialize it, along with the
+ // schema.
+ boolean checkSchema = configFile.isPresent();
+ if (checkSchema)
+ {
+ try
+ {
+ directoryServer.initializeJMX();
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_INITIALIZE_JMX;
+ String message = getMessage(msgID,
+ String.valueOf(configFile.getValue()),
+ e.getMessage());
+ System.err.println(message);
+ return 1;
+ }
+
+ try
+ {
+ directoryServer.initializeConfiguration(configClass.getValue(),
+ configFile.getValue());
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_INITIALIZE_CONFIG;
+ String message = getMessage(msgID,
+ String.valueOf(configFile.getValue()),
+ e.getMessage());
+ System.err.println(message);
+ return 1;
+ }
+
+ try
+ {
+ directoryServer.initializeSchema();
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_INITIALIZE_SCHEMA;
+ String message = getMessage(msgID,
+ String.valueOf(configFile.getValue()),
+ e.getMessage());
+ System.err.println(message);
+ return 1;
+ }
+ }
+
+
+ // Create the LDIF readers and writer from the arguments.
+ File source = new File(sourceFile.getValue());
+ if (! source.exists())
+ {
+ int msgID = MSGID_LDIFMODIFY_SOURCE_DOES_NOT_EXIST;
+ String message = getMessage(msgID, sourceFile.getValue());
+ System.err.println(message);
+ return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
+ }
+
+ LDIFImportConfig importConfig = new LDIFImportConfig(sourceFile.getValue());
+ LDIFReader sourceReader;
+ try
+ {
+ sourceReader = new LDIFReader(importConfig);
+ }
+ catch (IOException ioe)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_OPEN_SOURCE;
+ String message = getMessage(msgID, sourceFile.getValue(),
+ String.valueOf(ioe));
+ System.err.println(message);
+ return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
+ }
+
+
+ File changes = new File(changesFile.getValue());
+ if (! changes.exists())
+ {
+ int msgID = MSGID_LDIFMODIFY_CHANGES_DOES_NOT_EXIST;
+ String message = getMessage(msgID, changesFile.getValue());
+ System.err.println(message);
+ return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
+ }
+
+ importConfig = new LDIFImportConfig(changesFile.getValue());
+ LDIFReader changeReader;
+ try
+ {
+ changeReader = new LDIFReader(importConfig);
+ }
+ catch (IOException ioe)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_OPEN_CHANGES;
+ String message = getMessage(msgID, sourceFile.getValue());
+ System.err.println(message);
+ return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
+ }
+
+
+ LDIFExportConfig exportConfig =
+ new LDIFExportConfig(targetFile.getValue(),
+ ExistingFileBehavior.OVERWRITE);
+ LDIFWriter targetWriter;
+ try
+ {
+ targetWriter = new LDIFWriter(exportConfig);
+ }
+ catch (IOException ioe)
+ {
+ int msgID = MSGID_LDIFMODIFY_CANNOT_OPEN_TARGET;
+ String message = getMessage(msgID, sourceFile.getValue());
+ System.err.println(message);
+ return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
+ }
+
+
+ // Actually invoke the LDIF procesing.
+ LinkedList<String> errorList = new LinkedList<String>();
+ boolean successful;
+ try
+ {
+ successful = modifyLDIF(sourceReader, changeReader, targetWriter,
+ errorList);
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_LDIFMODIFY_ERROR_PROCESSING_LDIF;
+ String message = getMessage(msgID, String.valueOf(e));
+ System.err.println(message);
+
+ successful = false;
+ }
+
+ try
+ {
+ sourceReader.close();
+ } catch (Exception e) {}
+
+ try
+ {
+ changeReader.close();
+ } catch (Exception e) {}
+
+ try
+ {
+ targetWriter.close();
+ } catch (Exception e) {}
+
+ for (String s : errorList)
+ {
+ System.err.println(s);
+ }
+ return (successful ? 0 : 1);
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
index 6d65386..fa2ccd2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -1765,6 +1765,247 @@
/**
+ * Applies the provided modification to this entry. No schema
+ * checking will be performed.
+ *
+ * @param mod The modification to apply to this entry.
+ *
+ * @throws DirectoryException If a problem occurs while attempting
+ * to apply the modification. Note
+ * that even if a problem occurs, then
+ * the entry may have been altered in
+ * some way.
+ */
+ public void applyModification(Modification mod)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "applyModification",
+ String.valueOf(mod));
+
+ Attribute a = mod.getAttribute();
+ AttributeType t = a.getAttributeType();
+
+ // We'll need to handle changes to the objectclass attribute in a
+ // special way.
+ if (t.isObjectClassType())
+ {
+ LinkedHashMap<ObjectClass,String> ocs = new
+ LinkedHashMap<ObjectClass,String>();
+ for (AttributeValue v : a.getValues())
+ {
+ String ocName = v.getStringValue();
+ String lowerName = toLowerCase(ocName);
+ ObjectClass oc =
+ DirectoryServer.getObjectClass(lowerName, true);
+ ocs.put(oc, ocName);
+ }
+
+ switch (mod.getModificationType())
+ {
+ case ADD:
+ for (ObjectClass oc : ocs.keySet())
+ {
+ if (objectClasses.containsKey(oc))
+ {
+ int msgID = MSGID_ENTRY_DUPLICATE_VALUES;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(
+ ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+ message, msgID);
+ }
+ else
+ {
+ objectClasses.put(oc, ocs.get(oc));
+ }
+ }
+ break;
+
+ case DELETE:
+ for (ObjectClass oc : ocs.keySet())
+ {
+ if (objectClasses.remove(oc) == null)
+ {
+ int msgID = MSGID_ENTRY_NO_SUCH_VALUE;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(
+ ResultCode.NO_SUCH_ATTRIBUTE, message,
+ msgID);
+ }
+ }
+ break;
+
+ case REPLACE:
+ objectClasses = ocs;
+ break;
+
+ case INCREMENT:
+ int msgID = MSGID_ENTRY_OC_INCREMENT_NOT_SUPPORTED;
+ String message = getMessage(msgID);
+ throw new DirectoryException(
+ ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+
+ default:
+ msgID = MSGID_ENTRY_UNKNOWN_MODIFICATION_TYPE;
+ message = getMessage(msgID,
+ String.valueOf(mod.getModificationType()));
+ throw new DirectoryException(
+ ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+ }
+
+ return;
+ }
+
+ switch (mod.getModificationType())
+ {
+ case ADD:
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ addAttribute(a, duplicateValues);
+ if (! duplicateValues.isEmpty())
+ {
+ int msgID = MSGID_ENTRY_DUPLICATE_VALUES;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(
+ ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+ message, msgID);
+ }
+ break;
+
+ case DELETE:
+ LinkedList<AttributeValue> missingValues =
+ new LinkedList<AttributeValue>();
+ removeAttribute(a, missingValues);
+ if (! missingValues.isEmpty())
+ {
+ int msgID = MSGID_ENTRY_NO_SUCH_VALUE;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ message, msgID);
+ }
+ break;
+
+ case REPLACE:
+ removeAttribute(t, a.getOptions());
+
+ if (a.hasValue())
+ {
+ // We know that we won't have any duplicate values, so we
+ // don't kneed to worry about checking for them.
+ duplicateValues = new LinkedList<AttributeValue>();
+ addAttribute(a, duplicateValues);
+ }
+ break;
+
+ case INCREMENT:
+ List<Attribute> attrList = getAttribute(t);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ message, msgID);
+ }
+ else if (attrList.size() != 1)
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_MULTIPLE_VALUES;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(
+ ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+
+ LinkedHashSet<AttributeValue> values =
+ attrList.get(0).getValues();
+ if (values.isEmpty())
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ message, msgID);
+ }
+ else if (values.size() > 1)
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_MULTIPLE_VALUES;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(
+ ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+
+ LinkedHashSet<AttributeValue> newValues = a.getValues();
+ if (newValues.size() != 1)
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_INVALID_VALUE_COUNT;
+ String message = getMessage(msgID);
+ throw new DirectoryException(
+ ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+
+ long newValue;
+ try
+ {
+ String s = values.iterator().next().getStringValue();
+ long currentValue = Long.parseLong(s);
+
+ s = a.getValues().iterator().next().getStringValue();
+ long increment = Long.parseLong(s);
+
+ newValue = currentValue+increment;
+ }
+ catch (NumberFormatException nfe)
+ {
+ int msgID = MSGID_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT;
+ String message = getMessage(msgID);
+ throw new DirectoryException(
+ ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+
+ values.clear();
+ values.add(new AttributeValue(t, String.valueOf(newValue)));
+ break;
+
+ default:
+ int msgID = MSGID_ENTRY_UNKNOWN_MODIFICATION_TYPE;
+ String message =
+ getMessage(msgID,
+ String.valueOf(mod.getModificationType()));
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
+ }
+
+
+
+ /**
+ * Applies all of the provided modifications to this entry.
+ *
+ * @param mods The modifications to apply to this entry.
+ *
+ * @throws DirectoryException If a problem occurs while attempting
+ * to apply the modifications. Note
+ * that even if a problem occurs, then
+ * the entry may have been altered in
+ * some way.
+ */
+ public void applyModifications(List<Modification> mods)
+ throws DirectoryException
+ {
+ assert debugEnter(CLASS_NAME, "applyModifications",
+ String.valueOf(mods));
+
+ for (Modification m : mods)
+ {
+ applyModification(m);
+ }
+ }
+
+
+
+ /**
* Indicates whether this entry conforms to the server's schema
* requirements. The checks performed by this method include:
*
--
Gitblit v1.10.0