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

Matthew Swift
06.38.2012 90ff70d4c2a798957d2d6eabe367d0a271b794ca
Fix OPENDJ-382: Implement ldifdiff, ldifsearch, and ldifmodify tools for the SDK toolkit
9 files added
4 files modified
1277 ■■■■■ changed files
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifdiff.bat 32 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifmodify.bat 32 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifsearch.bat 32 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifdiff 37 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifmodify 38 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifsearch 37 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ConsoleApplication.java 27 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java 251 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java 378 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java 363 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ToolConstants.java 12 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties 35 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm 3 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifdiff.bat
New file
@@ -0,0 +1,32 @@
@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/opendj3/legal-notices/CDDLv1_0.txt
rem or http://forgerock.org/license/CDDLv1.0.html.
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/opendj3/legal-notices/CDDLv1_0.txt.  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      Copyright 2012 ForgeRock AS.
setlocal
set OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFDiff"
set SCRIPT_NAME=ldifdiff
for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifmodify.bat
New file
@@ -0,0 +1,32 @@
@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/opendj3/legal-notices/CDDLv1_0.txt
rem or http://forgerock.org/license/CDDLv1.0.html.
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/opendj3/legal-notices/CDDLv1_0.txt.  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      Copyright 2012 ForgeRock AS.
setlocal
set OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFModify"
set SCRIPT_NAME=ldifmodify
for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/ldifsearch.bat
New file
@@ -0,0 +1,32 @@
@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/opendj3/legal-notices/CDDLv1_0.txt
rem or http://forgerock.org/license/CDDLv1.0.html.
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/opendj3/legal-notices/CDDLv1_0.txt.  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      Copyright 2012 ForgeRock AS.
setlocal
set OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFSearch"
set SCRIPT_NAME=ldifsearch
for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifdiff
New file
@@ -0,0 +1,37 @@
#!/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/opendj3/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
# trunk/opendj3/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 2012 ForgeRock AS.
# This script may be used to compare LDIF files.
OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFDiff"
export OPENDJ_INVOKE_CLASS
SCRIPT_NAME="ldifdiff"
export SCRIPT_NAME
SCRIPT_DIR=`dirname "${0}"`
"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifmodify
New file
@@ -0,0 +1,38 @@
#!/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/opendj3/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
# trunk/opendj3/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 2012 ForgeRock AS.
# This script may be used to perform LDAP add, delete, modify, and modify DN
# operations against an LDIF file.
OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFModify"
export OPENDJ_INVOKE_CLASS
SCRIPT_NAME="ldifmodify"
export SCRIPT_NAME
SCRIPT_DIR=`dirname "${0}"`
"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/ldifsearch
New file
@@ -0,0 +1,37 @@
#!/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/opendj3/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
# trunk/opendj3/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 2012 ForgeRock AS.
# This script may be used to perform search operations against an LDIF file.
OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.LDIFSearch"
export OPENDJ_INVOKE_CLASS
SCRIPT_NAME="ldifsearch"
export SCRIPT_NAME
SCRIPT_DIR=`dirname "${0}"`
"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ConsoleApplication.java
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2012 ForgeRock AS
 *      Portions copyright 2011 Nemanja Lukić
 */
package com.forgerock.opendj.ldap.tools;
@@ -46,7 +46,7 @@
 */
abstract class ConsoleApplication
{
  private final PrintStream err = new PrintStream(System.out);
  private final PrintStream err = new PrintStream(System.err);
  private final BufferedReader reader = new BufferedReader(
      new InputStreamReader(System.in));
@@ -70,6 +70,29 @@
  /**
   * Closes the provided {@code Closeable} if it is not {@code null}.
   *
   * @param closeable
   *          The closeable to be closed.
   */
  final void closeIfNotNull(Closeable closeable)
  {
    if (closeable != null)
    {
      try
      {
        closeable.close();
      }
      catch (Exception ignored)
      {
        // Do nothing.
      }
    }
  }
  /**
   * Returns the application error stream.
   *
   * @return The application error stream.
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFDiff.java
New file
@@ -0,0 +1,251 @@
/*
 * 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/opendj3/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
 * trunk/opendj3/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 2012 ForgeRock AS
 */
package com.forgerock.opendj.ldap.tools;
import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
import java.io.*;
import java.util.List;
import org.forgerock.i18n.LocalizableException;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldif.LDIF;
import org.forgerock.opendj.ldif.LDIFChangeRecordWriter;
import org.forgerock.opendj.ldif.LDIFEntryReader;
/**
 * This utility can be used to compare two LDIF files and report the differences
 * in LDIF format.
 */
public final class LDIFDiff extends ConsoleApplication
{
  /**
   * The main method for LDIFDiff tool.
   *
   * @param args
   *          The command-line arguments provided to this program.
   */
  public static void main(final String[] args)
  {
    final int retCode = new LDIFDiff().run(args);
    System.exit(filterExitCode(retCode));
  }
  private LDIFDiff()
  {
    // Nothing to do.
  }
  private int run(final String[] args)
  {
    // Create the command-line argument parser for use with this
    // program.
    final LocalizableMessage toolDescription = INFO_LDIFDIFF_TOOL_DESCRIPTION
        .get();
    final ArgumentParser argParser = new ArgumentParser(
        LDIFDiff.class.getName(), toolDescription, false, true, 2, 2,
        "source target");
    final BooleanArgument showUsage;
    final StringArgument outputFilename;
    try
    {
      outputFilename = new StringArgument("outputFilename",
          OPTION_SHORT_OUTPUT_LDIF_FILENAME, OPTION_LONG_OUTPUT_LDIF_FILENAME,
          false, false, true, INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get(),
          "stdout", null,
          INFO_LDIFDIFF_DESCRIPTION_OUTPUT_FILENAME
              .get(INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get()));
      argParser.addArgument(outputFilename);
      showUsage = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
          OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get());
      argParser.addArgument(showUsage);
      argParser.setUsageArgument(showUsage, getOutputStream());
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    // Parse the command-line arguments provided to this program.
    try
    {
      argParser.parseArguments(args);
      // If we should just display usage or version information,
      // then print it and exit.
      if (argParser.usageOrVersionDisplayed())
      {
        return 0;
      }
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    InputStream sourceInputStream = null;
    InputStream targetInputStream = null;
    OutputStream outputStream = null;
    LDIFEntryReader sourceReader = null;
    LDIFEntryReader targetReader = null;
    LDIFChangeRecordWriter outputWriter = null;
    try
    {
      // First source file.
      final List<String> trailingArguments = argParser.getTrailingArguments();
      if (!trailingArguments.get(0).equals("-"))
      {
        try
        {
          sourceInputStream = new FileInputStream(trailingArguments.get(0));
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ
              .get(trailingArguments.get(0), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Patch file.
      if (!trailingArguments.get(1).equals("-"))
      {
        try
        {
          targetInputStream = new FileInputStream(trailingArguments.get(1));
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ
              .get(trailingArguments.get(1), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Output file.
      if (outputFilename.isPresent() && !outputFilename.getValue().equals("-"))
      {
        try
        {
          outputStream = new FileOutputStream(outputFilename.getValue());
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE
              .get(outputFilename.getValue(), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Default to stdin/stdout for all streams if not specified.
      if (sourceInputStream == null)
      {
        // Command line parameter was "-".
        sourceInputStream = System.in;
      }
      if (targetInputStream == null)
      {
        targetInputStream = System.in;
      }
      if (outputStream == null)
      {
        outputStream = System.out;
      }
      // Check that we are not attempting to read both the source and target
      // from stdin.
      if (sourceInputStream == targetInputStream)
      {
        final LocalizableMessage message = ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN
            .get();
        println(message);
        return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
      }
      // Perform the diff.
      sourceReader = new LDIFEntryReader(sourceInputStream);
      targetReader = new LDIFEntryReader(targetInputStream);
      outputWriter = new LDIFChangeRecordWriter(outputStream);
      LDIF.copyTo(LDIF.diff(sourceReader, targetReader), outputWriter);
    }
    catch (final IOException e)
    {
      if (e instanceof LocalizableException)
      {
        println(ERR_LDIFDIFF_DIFF_FAILED.get(((LocalizableException) e)
            .getMessageObject()));
      }
      else
      {
        println(ERR_LDIFDIFF_DIFF_FAILED.get(e.getLocalizedMessage()));
      }
      return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
    }
    finally
    {
      closeIfNotNull(sourceReader);
      closeIfNotNull(targetReader);
      closeIfNotNull(outputWriter);
      closeIfNotNull(sourceInputStream);
      closeIfNotNull(targetInputStream);
      closeIfNotNull(outputStream);
    }
    return ResultCode.SUCCESS.intValue();
  }
}
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFModify.java
New file
@@ -0,0 +1,378 @@
/*
 * 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/opendj3/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
 * trunk/opendj3/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 2012 ForgeRock AS
 */
package com.forgerock.opendj.ldap.tools;
import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
import java.io.*;
import java.util.List;
import org.forgerock.i18n.LocalizableException;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldif.*;
/**
 * A tool that can be used to issue update (Add/Delete/Modify/ModifyDN) requests
 * to a set of entries contained in an LDIF file.
 */
public final class LDIFModify extends ConsoleApplication
{
  /**
   * The main method for LDIFModify tool.
   *
   * @param args
   *          The command-line arguments provided to this program.
   */
  public static void main(final String[] args)
  {
    final int retCode = new LDIFModify().run(args);
    System.exit(filterExitCode(retCode));
  }
  private LDIFModify()
  {
    // Nothing to do.
  }
  private int run(final String[] args)
  {
    // Create the command-line argument parser for use with this
    // program.
    final LocalizableMessage toolDescription = INFO_LDIFMODIFY_TOOL_DESCRIPTION
        .get();
    final ArgumentParser argParser = new ArgumentParser(
        LDIFModify.class.getName(), toolDescription, false, true, 1, 2,
        "source [changes]");
    final BooleanArgument continueOnError;
    final BooleanArgument showUsage;
    final StringArgument outputFilename;
    try
    {
      outputFilename = new StringArgument("outputFilename",
          OPTION_SHORT_OUTPUT_LDIF_FILENAME, OPTION_LONG_OUTPUT_LDIF_FILENAME,
          false, false, true, INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get(),
          "stdout", null,
          INFO_LDIFMODIFY_DESCRIPTION_OUTPUT_FILENAME
              .get(INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get()));
      argParser.addArgument(outputFilename);
      continueOnError = new BooleanArgument("continueOnError", 'c',
          "continueOnError", INFO_DESCRIPTION_CONTINUE_ON_ERROR.get());
      continueOnError.setPropertyName("continueOnError");
      argParser.addArgument(continueOnError);
      showUsage = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
          OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get());
      argParser.addArgument(showUsage);
      argParser.setUsageArgument(showUsage, getOutputStream());
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    // Parse the command-line arguments provided to this program.
    try
    {
      argParser.parseArguments(args);
      // If we should just display usage or version information,
      // then print it and exit.
      if (argParser.usageOrVersionDisplayed())
      {
        return 0;
      }
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    InputStream sourceInputStream = null;
    InputStream changesInputStream = null;
    OutputStream outputStream = null;
    LDIFEntryReader sourceReader = null;
    LDIFChangeRecordReader changesReader = null;
    LDIFEntryWriter outputWriter = null;
    try
    {
      // First source file.
      final List<String> trailingArguments = argParser.getTrailingArguments();
      if (!trailingArguments.get(0).equals("-"))
      {
        try
        {
          sourceInputStream = new FileInputStream(trailingArguments.get(0));
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ
              .get(trailingArguments.get(0), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Patch file.
      if (trailingArguments.size() > 1 && !trailingArguments.get(1).equals("-"))
      {
        try
        {
          changesInputStream = new FileInputStream(trailingArguments.get(1));
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ
              .get(trailingArguments.get(1), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Output file.
      if (outputFilename.isPresent() && !outputFilename.getValue().equals("-"))
      {
        try
        {
          outputStream = new FileOutputStream(outputFilename.getValue());
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE
              .get(outputFilename.getValue(), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Default to stdin/stdout for all streams if not specified.
      if (sourceInputStream == null)
      {
        // Command line parameter was "-".
        sourceInputStream = System.in;
      }
      if (changesInputStream == null)
      {
        changesInputStream = System.in;
      }
      if (outputStream == null)
      {
        outputStream = System.out;
      }
      // Check that we are not attempting to read both the source and changes
      // from stdin.
      if (sourceInputStream == changesInputStream)
      {
        final LocalizableMessage message = ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN
            .get();
        println(message);
        return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
      }
      // Apply the changes.
      sourceReader = new LDIFEntryReader(sourceInputStream);
      changesReader = new LDIFChangeRecordReader(changesInputStream);
      outputWriter = new LDIFEntryWriter(outputStream);
      final RejectedChangeRecordListener listener = new RejectedChangeRecordListener()
      {
        public Entry handleDuplicateEntry(final AddRequest change,
            final Entry existingEntry) throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(change,
                existingEntry);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
          return change;
        }
        public Entry handleDuplicateEntry(final ModifyDNRequest change,
            final Entry existingEntry, final Entry renamedEntry)
            throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleDuplicateEntry(change,
                existingEntry, renamedEntry);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
          return renamedEntry;
        }
        public void handleRejectedChangeRecord(final AddRequest change,
            final LocalizableMessage reason) throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(
                change, reason);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
        }
        public void handleRejectedChangeRecord(final DeleteRequest change,
            final LocalizableMessage reason) throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(
                change, reason);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
        }
        public void handleRejectedChangeRecord(final ModifyDNRequest change,
            final LocalizableMessage reason) throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(
                change, reason);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
        }
        public void handleRejectedChangeRecord(final ModifyRequest change,
            final LocalizableMessage reason) throws DecodeException
        {
          try
          {
            RejectedChangeRecordListener.FAIL_FAST.handleRejectedChangeRecord(
                change, reason);
          }
          catch (final DecodeException e)
          {
            logErrorOrFail(e);
          }
        }
        private void logErrorOrFail(final DecodeException e)
            throws DecodeException
        {
          if (continueOnError.isPresent())
          {
            println(e.getMessageObject());
          }
          else
          {
            throw e;
          }
        }
      };
      LDIF.copyTo(LDIF.patch(sourceReader, changesReader, listener),
          outputWriter);
    }
    catch (final IOException e)
    {
      if (e instanceof LocalizableException)
      {
        println(ERR_LDIFMODIFY_PATCH_FAILED.get(((LocalizableException) e)
            .getMessageObject()));
      }
      else
      {
        println(ERR_LDIFMODIFY_PATCH_FAILED.get(e.getLocalizedMessage()));
      }
      return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
    }
    finally
    {
      closeIfNotNull(sourceReader);
      closeIfNotNull(changesReader);
      closeIfNotNull(outputWriter);
      closeIfNotNull(sourceInputStream);
      closeIfNotNull(changesInputStream);
      closeIfNotNull(outputStream);
    }
    return ResultCode.SUCCESS.intValue();
  }
}
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/LDIFSearch.java
New file
@@ -0,0 +1,363 @@
/*
 * 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/opendj3/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
 * trunk/opendj3/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 2012 ForgeRock AS
 */
package com.forgerock.opendj.ldap.tools;
import static com.forgerock.opendj.ldap.tools.ToolConstants.*;
import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import static com.forgerock.opendj.ldap.tools.Utils.filterExitCode;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.i18n.LocalizableException;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldif.LDIF;
import org.forgerock.opendj.ldif.LDIFEntryReader;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
/**
 * This utility can be used to perform search operations against data in an LDIF
 * file.
 */
public final class LDIFSearch extends ConsoleApplication
{
  /**
   * The main method for LDIFSearch tool.
   *
   * @param args
   *          The command-line arguments provided to this program.
   */
  public static void main(final String[] args)
  {
    final int retCode = new LDIFSearch().run(args);
    System.exit(filterExitCode(retCode));
  }
  private LDIFSearch()
  {
    // Nothing to do.
  }
  private int run(final String[] args)
  {
    // Create the command-line argument parser for use with this
    // program.
    final LocalizableMessage toolDescription = INFO_LDIFSEARCH_TOOL_DESCRIPTION
        .get();
    final ArgumentParser argParser = new ArgumentParser(
        LDIFSearch.class.getName(), toolDescription, false, true, 1, 0,
        "source [filter] [attributes ...]");
    final BooleanArgument showUsage;
    final StringArgument outputFilename;
    final BooleanArgument typesOnly;
    final IntegerArgument timeLimit;
    final StringArgument filename;
    final StringArgument baseDN;
    final MultiChoiceArgument<SearchScope> searchScope;
    final IntegerArgument sizeLimit;
    try
    {
      outputFilename = new StringArgument("outputFilename",
          OPTION_SHORT_OUTPUT_LDIF_FILENAME, OPTION_LONG_OUTPUT_LDIF_FILENAME,
          false, false, true, INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get(),
          "stdout", null,
          INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILENAME
              .get(INFO_OUTPUT_LDIF_FILE_PLACEHOLDER.get()));
      argParser.addArgument(outputFilename);
      baseDN = new StringArgument("baseDN", OPTION_SHORT_BASEDN,
          OPTION_LONG_BASEDN, true, false, true, INFO_BASEDN_PLACEHOLDER.get(),
          null, null, INFO_SEARCH_DESCRIPTION_BASEDN.get());
      baseDN.setPropertyName(OPTION_LONG_BASEDN);
      argParser.addArgument(baseDN);
      searchScope = new MultiChoiceArgument<SearchScope>("searchScope", 's',
          "searchScope", false, true, INFO_SEARCH_SCOPE_PLACEHOLDER.get(),
          SearchScope.values(), false,
          INFO_SEARCH_DESCRIPTION_SEARCH_SCOPE.get());
      searchScope.setPropertyName("searchScope");
      searchScope.setDefaultValue(SearchScope.WHOLE_SUBTREE);
      argParser.addArgument(searchScope);
      filename = new StringArgument("filename", OPTION_SHORT_FILENAME,
          OPTION_LONG_FILENAME, false, false, true,
          INFO_FILE_PLACEHOLDER.get(), null, null,
          INFO_SEARCH_DESCRIPTION_FILENAME.get());
      searchScope.setPropertyName(OPTION_LONG_FILENAME);
      argParser.addArgument(filename);
      typesOnly = new BooleanArgument("typesOnly", 'A', "typesOnly",
          INFO_DESCRIPTION_TYPES_ONLY.get());
      typesOnly.setPropertyName("typesOnly");
      argParser.addArgument(typesOnly);
      sizeLimit = new IntegerArgument("sizeLimit", 'z', "sizeLimit", false,
          false, true, INFO_SIZE_LIMIT_PLACEHOLDER.get(), 0, null,
          INFO_SEARCH_DESCRIPTION_SIZE_LIMIT.get());
      sizeLimit.setPropertyName("sizeLimit");
      argParser.addArgument(sizeLimit);
      timeLimit = new IntegerArgument("timeLimit", 'l', "timeLimit", false,
          false, true, INFO_TIME_LIMIT_PLACEHOLDER.get(), 0, null,
          INFO_SEARCH_DESCRIPTION_TIME_LIMIT.get());
      timeLimit.setPropertyName("timeLimit");
      argParser.addArgument(timeLimit);
      showUsage = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
          OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get());
      argParser.addArgument(showUsage);
      argParser.setUsageArgument(showUsage, getOutputStream());
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    // Parse the command-line arguments provided to this program.
    try
    {
      argParser.parseArguments(args);
      // If we should just display usage or version information,
      // then print it and exit.
      if (argParser.usageOrVersionDisplayed())
      {
        return 0;
      }
    }
    catch (final ArgumentException ae)
    {
      final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae
          .getMessage());
      println(message);
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    final List<Filter> filters = new LinkedList<Filter>();
    final List<String> attributes = new LinkedList<String>();
    final List<String> trailingArguments = argParser.getTrailingArguments();
    if (trailingArguments.size() > 1)
    {
      final List<String> filterAndAttributeStrings = trailingArguments.subList(
          1, trailingArguments.size());
      // the list of trailing arguments should be structured as follow:
      // - If a filter file is present, trailing arguments are
      // considered as attributes
      // - If filter file is not present, the first trailing argument is
      // considered the filter, the other as attributes.
      if (!filename.isPresent())
      {
        final String filterString = filterAndAttributeStrings.remove(0);
        try
        {
          filters.add(Filter.valueOf(filterString));
        }
        catch (final LocalizedIllegalArgumentException e)
        {
          println(e.getMessageObject());
          return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
        }
      }
      // The rest are attributes
      for (final String s : filterAndAttributeStrings)
      {
        attributes.add(s);
      }
    }
    if (filename.isPresent())
    {
      // Read the filter strings.
      BufferedReader in = null;
      try
      {
        in = new BufferedReader(new FileReader(filename.getValue()));
        String line = null;
        while ((line = in.readLine()) != null)
        {
          if (line.trim().equals(""))
          {
            // ignore empty lines.
            continue;
          }
          final Filter ldapFilter = Filter.valueOf(line);
          filters.add(ldapFilter);
        }
      }
      catch (final LocalizedIllegalArgumentException e)
      {
        println(e.getMessageObject());
        return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
      }
      catch (final IOException e)
      {
        println(LocalizableMessage.raw(e.toString()));
        return ResultCode.CLIENT_SIDE_FILTER_ERROR.intValue();
      }
      finally
      {
        closeIfNotNull(in);
      }
    }
    if (filters.isEmpty())
    {
      println(ERR_SEARCH_NO_FILTERS.get());
      println(argParser.getUsageMessage());
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    final SearchRequest search;
    try
    {
      final SearchScope scope = searchScope.getTypedValue();
      search = Requests
          .newSearchRequest(DN.valueOf(baseDN.getValue()), scope,
              filters.get(0), attributes.toArray(new String[attributes.size()]))
          .setTypesOnly(typesOnly.isPresent())
          .setTimeLimit(timeLimit.getIntValue())
          .setSizeLimit(sizeLimit.getIntValue());
    }
    catch (final ArgumentException e)
    {
      println(e.getMessageObject());
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      println(e.getMessageObject());
      return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
    }
    InputStream sourceInputStream = null;
    OutputStream outputStream = null;
    LDIFEntryReader sourceReader = null;
    LDIFEntryWriter outputWriter = null;
    try
    {
      // First source file.
      if (!trailingArguments.get(0).equals("-"))
      {
        try
        {
          sourceInputStream = new FileInputStream(trailingArguments.get(0));
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ
              .get(trailingArguments.get(0), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Output file.
      if (outputFilename.isPresent() && !outputFilename.getValue().equals("-"))
      {
        try
        {
          outputStream = new FileOutputStream(outputFilename.getValue());
        }
        catch (final FileNotFoundException e)
        {
          final LocalizableMessage message = ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE
              .get(outputFilename.getValue(), e.getLocalizedMessage());
          println(message);
          return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
        }
      }
      // Default to stdin/stdout for all streams if not specified.
      if (sourceInputStream == null)
      {
        // Command line parameter was "-".
        sourceInputStream = System.in;
      }
      if (outputStream == null)
      {
        outputStream = System.out;
      }
      // Perform the search.
      sourceReader = new LDIFEntryReader(sourceInputStream);
      outputWriter = new LDIFEntryWriter(outputStream);
      LDIF.copyTo(LDIF.search(sourceReader, search), outputWriter);
    }
    catch (final IOException e)
    {
      if (e instanceof LocalizableException)
      {
        println(ERR_LDIFSEARCH_FAILED.get(((LocalizableException) e)
            .getMessageObject()));
      }
      else
      {
        println(ERR_LDIFSEARCH_FAILED.get(e.getLocalizedMessage()));
      }
      return ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
    }
    finally
    {
      closeIfNotNull(sourceReader);
      closeIfNotNull(outputWriter);
      closeIfNotNull(sourceInputStream);
      closeIfNotNull(outputStream);
    }
    return ResultCode.SUCCESS.intValue();
  }
}
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ToolConstants.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 */
package com.forgerock.opendj.ldap.tools;
@@ -608,6 +609,17 @@
   */
  static final String LIST_TABLE_SEPARATOR = ":";
  /**
   *
   * The value for the short option output LDIF filename.
   */
  static final char OPTION_SHORT_OUTPUT_LDIF_FILENAME= 'o';
  /**
   * The value for the long option output LDIF filename.
   */
  static final String OPTION_LONG_OUTPUT_LDIF_FILENAME = "outputLDIF";
  // Prevent instantiation.
opendj3/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
@@ -23,12 +23,7 @@
#
#
#      Copyright 2010 Sun Microsystems, Inc.
#
#
# Core messages
#
#
# Protocol messages
#      Portions copyright 2012 ForgeRock AS.
#
#
# Utility messages
@@ -246,6 +241,8 @@
 filter was invalid:  %s
ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ=An error occurred while \
 attempting to open the LDIF file %s for reading:  %s
ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE=An error occurred while \
 attempting to open the LDIF file %s for writing:  %s
ERR_LDIF_FILE_READ_ERROR=An error occurred while attempting to read \
 the contents of LDIF file %s:  %s
INFO_BIND_PASSWORD_EXPIRED=# Your password has expired
@@ -444,6 +441,26 @@
  options. A search operation may be used to retrieve the bind DN by \
  specifying the base DN and a filter. The retrieved entry DN will be appended \
  as the last argument in the argument list when evaluating format strings.
INFO_OUTPUT_LDIF_FILE_PLACEHOLDER={file}
INFO_LDIFMODIFY_DESCRIPTION_OUTPUT_FILENAME=Write updated entries to %s \
 instead of stdout
INFO_LDIFDIFF_DESCRIPTION_OUTPUT_FILENAME=Write differences to %s \
 instead of stdout
INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILENAME=Write search results to %s \
 instead of stdout
ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
 LDIF and changes LDIF
ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
 LDIF and target LDIF
ERR_LDIFMODIFY_PATCH_FAILED=The changes could not be applied for the following \
 reason: %s
ERR_LDIFDIFF_DIFF_FAILED=The differences could not be computed for the following \
 reason: %s
ERR_LDIFSEARCH_FAILED=The search could not be performed for the following \
 reason: %s
INFO_LDIFMODIFY_TOOL_DESCRIPTION=This utility can be used to apply a set of \
 modify, add, and delete operations to entries contained in an LDIF file
INFO_LDIFDIFF_TOOL_DESCRIPTION=This utility can be used to compare two LDIF \
 files and report the differences in LDIF format
INFO_LDIFSEARCH_TOOL_DESCRIPTION=This utility can be used to perform search \
 operations against entries contained in an LDIF file
opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm
@@ -38,6 +38,9 @@
       <dt>ldapmodify</dt><dd>perform LDAP modify, add, delete, mod DN operations</dd>
       <dt>ldappasswordmodify</dt><dd>perform LDAP password modifications</dd>
       <dt>ldapsearch</dt><dd>perform LDAP search operations</dd>
       <dt>ldifmodify</dt><dd>perform LDAP modify, add, delete, mod DN operations against entries contained in an LDIF file</dd>
       <dt>ldifsearch</dt><dd>perform search operations against entries contained in an LDIF file</dd>
       <dt>ldifdiff</dt><dd>compare two LDIF files and report the differences in LDIF format</dd>
       <dt>modrate</dt><dd>measure modification throughput and response time</dd>
       <dt>searchrate</dt><dd>measure search throughput and response time</dd>
      </dl>