Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2016-2019 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright (C) 2016-2019 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.ldap.sdk.unboundidds.tools;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.ldap.sdk.AddRequest;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DeleteRequest;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPRequest;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.LDAPSearchException;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ModifyDNRequest;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldap.sdk.controls.AssertionRequestControl;
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl;
import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl;
import com.unboundid.ldap.sdk.controls.PostReadRequestControl;
import com.unboundid.ldap.sdk.controls.PreReadRequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl;
import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl;
import com.unboundid.ldap.sdk.controls.TransactionSpecificationRequestControl;
import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedRequest;
import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedResult;
import com.unboundid.ldap.sdk.extensions.EndTransactionExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel;
import com.unboundid.ldap.sdk.unboundidds.controls.
AssuredReplicationRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
AssuredReplicationRemoteLevel;
import com.unboundid.ldap.sdk.unboundidds.controls.
GeneratePasswordRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
GetAuthorizationEntryRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
GetBackendSetIDRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
GetUserResourceLimitsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.GetServerIDRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.HardDeleteRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
IgnoreNoUserModificationRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
NameWithEntryUUIDRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
OperationPurposeRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
PasswordUpdateBehaviorRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
PasswordUpdateBehaviorRequestControlProperties;
import com.unboundid.ldap.sdk.unboundidds.controls.
PasswordValidationDetailsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
ReplicationRepairRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
RouteToBackendSetRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.RouteToServerRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.SoftDeleteRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
SuppressOperationalAttributeUpdateRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
SuppressReferentialIntegrityUpdatesRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessMultipleAttributeBehavior;
import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
UniquenessRequestControlProperties;
import com.unboundid.ldap.sdk.unboundidds.controls.SuppressType;
import com.unboundid.ldap.sdk.unboundidds.controls.UndeleteRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessValidationLevel;
import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateErrorBehavior;
import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.extensions.
StartAdministrativeSessionExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.extensions.
StartAdministrativeSessionPostConnectProcessor;
import com.unboundid.ldif.LDIFAddChangeRecord;
import com.unboundid.ldif.LDIFChangeRecord;
import com.unboundid.ldif.LDIFDeleteChangeRecord;
import com.unboundid.ldif.LDIFException;
import com.unboundid.ldif.LDIFModifyChangeRecord;
import com.unboundid.ldif.LDIFModifyDNChangeRecord;
import com.unboundid.ldif.LDIFReader;
import com.unboundid.ldif.LDIFWriter;
import com.unboundid.ldif.TrailingSpaceBehavior;
import com.unboundid.util.Debug;
import com.unboundid.util.DNFileReader;
import com.unboundid.util.FilterFileReader;
import com.unboundid.util.FixedRateBarrier;
import com.unboundid.util.LDAPCommandLineTool;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.SubtreeDeleter;
import com.unboundid.util.SubtreeDeleterResult;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.ControlArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.DurationArgument;
import com.unboundid.util.args.FileArgument;
import com.unboundid.util.args.FilterArgument;
import com.unboundid.util.args.IntegerArgument;
import com.unboundid.util.args.StringArgument;
import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;
/**
* This class provides an implementation of an LDAP command-line tool that may
* be used to apply changes to a directory server. The changes to apply (which
* may include add, delete, modify, and modify DN operations) will be read in
* LDIF form, either from standard input or a specified file or set of files.
* This is a much more full-featured tool than the
* {@link com.unboundid.ldap.sdk.examples.LDAPModify} tool
*
*
* NOTE: This class, and other classes within the
* {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
* supported for use against Ping Identity, UnboundID, and
* Nokia/Alcatel-Lucent 8661 server products. These classes provide support
* for proprietary functionality or for external specifications that are not
* considered stable or mature enough to be guaranteed to work in an
* interoperable way with other types of LDAP servers.
*
*/
@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
public final class LDAPModify
extends LDAPCommandLineTool
implements UnsolicitedNotificationHandler
{
/**
* The column at which output should be wrapped.
*/
private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
/**
* The name of the attribute type used to specify a password in the
* authentication password syntax as described in RFC 3112.
*/
private static final String ATTR_AUTH_PASSWORD = "authPassword";
/**
* The name of the attribute type used to specify the DN of the soft-deleted
* entry to be restored via an undelete operation.
*/
private static final String ATTR_UNDELETE_FROM_DN = "ds-undelete-from-dn";
/**
* The name of the attribute type used to specify a password in the
* userPassword syntax.
*/
private static final String ATTR_USER_PASSWORD = "userPassword";
/**
* The long identifier for the argument used to specify the desired assured
* replication local level.
*/
private static final String ARG_ASSURED_REPLICATION_LOCAL_LEVEL =
"assuredReplicationLocalLevel";
/**
* The long identifier for the argument used to specify the desired assured
* replication remote level.
*/
private static final String ARG_ASSURED_REPLICATION_REMOTE_LEVEL =
"assuredReplicationRemoteLevel";
/**
* The long identifier for the argument used to specify the desired assured
* timeout.
*/
private static final String ARG_ASSURED_REPLICATION_TIMEOUT =
"assuredReplicationTimeout";
/**
* The long identifier for the argument used to specify the path to an LDIF
* file containing changes to apply.
*/
private static final String ARG_LDIF_FILE = "ldifFile";
/**
* The long identifier for the argument used to specify the simple paged
* results page size to use when modifying entries that match a provided
* filter.
*/
private static final String ARG_SEARCH_PAGE_SIZE = "searchPageSize";
// The set of arguments supported by this program.
private BooleanArgument allowUndelete = null;
private BooleanArgument assuredReplication = null;
private BooleanArgument authorizationIdentity = null;
private BooleanArgument clientSideSubtreeDelete = null;
private BooleanArgument continueOnError = null;
private BooleanArgument defaultAdd = null;
private BooleanArgument dryRun = null;
private BooleanArgument followReferrals = null;
private BooleanArgument generatePassword = null;
private BooleanArgument getBackendSetID = null;
private BooleanArgument getServerID = null;
private BooleanArgument getUserResourceLimits = null;
private BooleanArgument hardDelete = null;
private BooleanArgument ignoreNoUserModification = null;
private BooleanArgument manageDsaIT = null;
private BooleanArgument nameWithEntryUUID = null;
private BooleanArgument noOperation = null;
private BooleanArgument passwordValidationDetails = null;
private BooleanArgument permissiveModify = null;
private BooleanArgument purgeCurrentPassword = null;
private BooleanArgument replicationRepair = null;
private BooleanArgument retireCurrentPassword = null;
private BooleanArgument retryFailedOperations = null;
private BooleanArgument softDelete = null;
private BooleanArgument stripTrailingSpaces = null;
private BooleanArgument serverSideSubtreeDelete = null;
private BooleanArgument suppressReferentialIntegrityUpdates = null;
private BooleanArgument useAdministrativeSession = null;
private BooleanArgument usePasswordPolicyControl = null;
private BooleanArgument useTransaction = null;
private BooleanArgument verbose = null;
private ControlArgument addControl = null;
private ControlArgument bindControl = null;
private ControlArgument deleteControl = null;
private ControlArgument modifyControl = null;
private ControlArgument modifyDNControl = null;
private ControlArgument operationControl = null;
private DNArgument modifyEntryWithDN = null;
private DNArgument proxyV1As = null;
private DNArgument uniquenessBaseDN = null;
private DurationArgument assuredReplicationTimeout = null;
private FileArgument encryptionPassphraseFile = null;
private FileArgument ldifFile = null;
private FileArgument modifyEntriesMatchingFiltersFromFile = null;
private FileArgument modifyEntriesWithDNsFromFile = null;
private FileArgument rejectFile = null;
private FilterArgument assertionFilter = null;
private FilterArgument modifyEntriesMatchingFilter = null;
private FilterArgument uniquenessFilter = null;
private IntegerArgument ratePerSecond = null;
private IntegerArgument searchPageSize = null;
private StringArgument assuredReplicationLocalLevel = null;
private StringArgument assuredReplicationRemoteLevel = null;
private StringArgument characterSet = null;
private StringArgument getAuthorizationEntryAttribute = null;
private StringArgument multiUpdateErrorBehavior = null;
private StringArgument operationPurpose = null;
private StringArgument passwordUpdateBehavior = null;
private StringArgument postReadAttribute = null;
private StringArgument preReadAttribute = null;
private StringArgument proxyAs = null;
private StringArgument routeToBackendSet = null;
private StringArgument routeToServer = null;
private StringArgument suppressOperationalAttributeUpdates = null;
private StringArgument uniquenessAttribute = null;
private StringArgument uniquenessMultipleAttributeBehavior = null;
private StringArgument uniquenessPostCommitValidationLevel = null;
private StringArgument uniquenessPreCommitValidationLevel = null;
// Indicates whether we've written anything to the reject writer yet.
private final AtomicBoolean rejectWritten;
// The input stream from to use for standard input.
private final InputStream in;
// The route to backend set request controls to include in write requests.
private final List
routeToBackendSetRequestControls = new ArrayList<>(10);
/**
* Runs this tool with the provided command-line arguments. It will use the
* JVM-default streams for standard input, output, and error.
*
* @param args The command-line arguments to provide to this program.
*/
public static void main(final String... args)
{
final ResultCode resultCode = main(System.in, System.out, System.err, args);
if (resultCode != ResultCode.SUCCESS)
{
System.exit(Math.min(resultCode.intValue(), 255));
}
}
/**
* Runs this tool with the provided streams and command-line arguments.
*
* @param in The input stream to use for standard input. If this is
* {@code null}, then no standard input will be used.
* @param out The output stream to use for standard output. If this is
* {@code null}, then standard output will be suppressed.
* @param err The output stream to use for standard error. If this is
* {@code null}, then standard error will be suppressed.
* @param args The command-line arguments provided to this program.
*
* @return The result code obtained when running the tool. Any result code
* other than {@link ResultCode#SUCCESS} indicates an error.
*/
public static ResultCode main(final InputStream in, final OutputStream out,
final OutputStream err, final String... args)
{
final LDAPModify tool = new LDAPModify(in, out, err);
return tool.runTool(args);
}
/**
* Creates a new instance of this tool with the provided streams. Standard
* input will not be available.
*
* @param out The output stream to use for standard output. If this is
* {@code null}, then standard output will be suppressed.
* @param err The output stream to use for standard error. If this is
* {@code null}, then standard error will be suppressed.
*/
public LDAPModify(final OutputStream out, final OutputStream err)
{
this(null, out, err);
}
/**
* Creates a new instance of this tool with the provided streams.
*
* @param in The input stream to use for standard input. If this is
* {@code null}, then no standard input will be used.
* @param out The output stream to use for standard output. If this is
* {@code null}, then standard output will be suppressed.
* @param err The output stream to use for standard error. If this is
* {@code null}, then standard error will be suppressed.
*/
public LDAPModify(final InputStream in, final OutputStream out,
final OutputStream err)
{
super(out, err);
if (in == null)
{
this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES);
}
else
{
this.in = in;
}
rejectWritten = new AtomicBoolean(false);
}
/**
* {@inheritDoc}
*/
@Override()
public String getToolName()
{
return "ldapmodify";
}
/**
* {@inheritDoc}
*/
@Override()
public String getToolDescription()
{
return INFO_LDAPMODIFY_TOOL_DESCRIPTION.get(ARG_LDIF_FILE);
}
/**
* {@inheritDoc}
*/
@Override()
public String getToolVersion()
{
return Version.NUMERIC_VERSION_STRING;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsInteractiveMode()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean defaultsToInteractiveMode()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsPropertiesFile()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsOutputFile()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
protected boolean defaultToPromptForBindPassword()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
protected boolean includeAlternateLongIdentifiers()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
protected boolean supportsSSLDebugging()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
protected boolean logToolInvocationByDefault()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public void addNonLDAPArguments(final ArgumentParser parser)
throws ArgumentException
{
ldifFile = new FileArgument('f', ARG_LDIF_FILE, false, -1, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_LDIF_FILE.get(), true, true, true,
false);
ldifFile.addLongIdentifier("filename", true);
ldifFile.addLongIdentifier("ldif-file", true);
ldifFile.addLongIdentifier("file-name", true);
ldifFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(ldifFile);
encryptionPassphraseFile = new FileArgument(null,
"encryptionPassphraseFile", false, 1, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true,
true, false);
encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file",
true);
encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true);
encryptionPassphraseFile.addLongIdentifier("encryption-password-file",
true);
encryptionPassphraseFile.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(encryptionPassphraseFile);
characterSet = new StringArgument('i', "characterSet", false, 1,
INFO_LDAPMODIFY_PLACEHOLDER_CHARSET.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_CHARACTER_SET.get(), "UTF-8");
characterSet.addLongIdentifier("encoding", true);
characterSet.addLongIdentifier("character-set", true);
characterSet.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(characterSet);
rejectFile = new FileArgument('R', "rejectFile", false, 1, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_REJECT_FILE.get(), false, true, true,
false);
rejectFile.addLongIdentifier("reject-file", true);
rejectFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(rejectFile);
verbose = new BooleanArgument('v', "verbose", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_VERBOSE.get());
verbose.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(verbose);
modifyEntriesMatchingFilter = new FilterArgument(null,
"modifyEntriesMatchingFilter", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRIES_MATCHING_FILTER.get(
ARG_SEARCH_PAGE_SIZE));
modifyEntriesMatchingFilter.addLongIdentifier(
"modify-entries-matching-filter", true);
modifyEntriesMatchingFilter.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(modifyEntriesMatchingFilter);
modifyEntriesMatchingFiltersFromFile = new FileArgument(null,
"modifyEntriesMatchingFiltersFromFile", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_FILTER_FILE.get(
ARG_SEARCH_PAGE_SIZE), true, false, true, false);
modifyEntriesMatchingFiltersFromFile.addLongIdentifier(
"modify-entries-matching-filters-from-file", true);
modifyEntriesMatchingFiltersFromFile.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(modifyEntriesMatchingFiltersFromFile);
modifyEntryWithDN = new DNArgument(null, "modifyEntryWithDN", false, 0,
null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRY_DN.get());
modifyEntryWithDN.addLongIdentifier("modify-entry-with-dn", true);
modifyEntryWithDN.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(modifyEntryWithDN);
modifyEntriesWithDNsFromFile = new FileArgument(null,
"modifyEntriesWithDNsFromFile", false, 0,
null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_FILE.get(), true,
false, true, false);
modifyEntriesWithDNsFromFile.addLongIdentifier(
"modify-entries-with-dns-from-file", true);
modifyEntriesWithDNsFromFile.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(modifyEntriesWithDNsFromFile);
searchPageSize = new IntegerArgument(null, ARG_SEARCH_PAGE_SIZE, false, 1,
null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_SEARCH_PAGE_SIZE.get(
modifyEntriesMatchingFilter.getIdentifierString(),
modifyEntriesMatchingFiltersFromFile.getIdentifierString()),
1, Integer.MAX_VALUE);
searchPageSize.addLongIdentifier("search-page-size", true);
searchPageSize.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(searchPageSize);
retryFailedOperations = new BooleanArgument(null, "retryFailedOperations",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get());
retryFailedOperations.addLongIdentifier("retry-failed-operations", true);
retryFailedOperations.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(retryFailedOperations);
dryRun = new BooleanArgument('n', "dryRun", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_DRY_RUN.get());
dryRun.addLongIdentifier("dry-run", true);
dryRun.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(dryRun);
defaultAdd = new BooleanArgument('a', "defaultAdd", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_DEFAULT_ADD.get());
defaultAdd.addLongIdentifier("default-add", true);
defaultAdd.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(defaultAdd);
continueOnError = new BooleanArgument('c', "continueOnError", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get());
continueOnError.addLongIdentifier("continue-on-error", true);
continueOnError.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(continueOnError);
stripTrailingSpaces = new BooleanArgument(null, "stripTrailingSpaces", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_STRIP_TRAILING_SPACES.get());
stripTrailingSpaces.addLongIdentifier("strip-trailing-spaces", true);
stripTrailingSpaces.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
parser.addArgument(stripTrailingSpaces);
followReferrals = new BooleanArgument(null, "followReferrals", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_FOLLOW_REFERRALS.get());
followReferrals.addLongIdentifier("follow-referrals", true);
followReferrals.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(followReferrals);
proxyAs = new StringArgument('Y', "proxyAs", false, 1,
INFO_PLACEHOLDER_AUTHZID.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_AS.get());
proxyAs.addLongIdentifier("proxyV2As", true);
proxyAs.addLongIdentifier("proxy-as", true);
proxyAs.addLongIdentifier("proxy-v2-as", true);
proxyAs.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(proxyAs);
proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_V1_AS.get());
proxyV1As.addLongIdentifier("proxy-v1-as", true);
proxyV1As.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(proxyV1As);
useAdministrativeSession = new BooleanArgument(null,
"useAdministrativeSession", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_ADMIN_SESSION.get());
useAdministrativeSession.addLongIdentifier("use-administrative-session",
true);
useAdministrativeSession.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(useAdministrativeSession);
operationPurpose = new StringArgument(null, "operationPurpose", false, 1,
INFO_PLACEHOLDER_PURPOSE.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_OPERATION_PURPOSE.get());
operationPurpose.addLongIdentifier("operation-purpose", true);
operationPurpose.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(operationPurpose);
manageDsaIT = new BooleanArgument(null, "useManageDsaIT", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_MANAGE_DSA_IT.get());
manageDsaIT.addLongIdentifier("manageDsaIT", true);
manageDsaIT.addLongIdentifier("use-manage-dsa-it", true);
manageDsaIT.addLongIdentifier("manage-dsa-it", true);
manageDsaIT.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(manageDsaIT);
useTransaction = new BooleanArgument(null, "useTransaction", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_TRANSACTION.get());
useTransaction.addLongIdentifier("use-transaction", true);
useTransaction.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(useTransaction);
final Set multiUpdateErrorBehaviorAllowedValues =
StaticUtils.setOf("atomic", "abort-on-error", "continue-on-error");
multiUpdateErrorBehavior = new StringArgument(null,
"multiUpdateErrorBehavior", false, 1,
"{atomic|abort-on-error|continue-on-error}",
INFO_LDAPMODIFY_ARG_DESCRIPTION_MULTI_UPDATE_ERROR_BEHAVIOR.get(),
multiUpdateErrorBehaviorAllowedValues);
multiUpdateErrorBehavior.addLongIdentifier("multi-update-error-behavior",
true);
multiUpdateErrorBehavior.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(multiUpdateErrorBehavior);
assertionFilter = new FilterArgument(null, "assertionFilter", false, 1,
INFO_PLACEHOLDER_FILTER.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSERTION_FILTER.get());
assertionFilter.addLongIdentifier("assertion-filter", true);
assertionFilter.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(assertionFilter);
authorizationIdentity = new BooleanArgument('E',
"authorizationIdentity", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_AUTHZ_IDENTITY.get());
authorizationIdentity.addLongIdentifier("reportAuthzID", true);
authorizationIdentity.addLongIdentifier("authorization-identity", true);
authorizationIdentity.addLongIdentifier("report-authzID", true);
authorizationIdentity.addLongIdentifier("report-authz-id", true);
authorizationIdentity.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(authorizationIdentity);
generatePassword = new BooleanArgument(null, "generatePassword", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_GENERATE_PASSWORD.get());
generatePassword.addLongIdentifier("generatePW", true);
generatePassword.addLongIdentifier("generate-password", true);
generatePassword.addLongIdentifier("generate-pw", true);
generatePassword.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(generatePassword);
getAuthorizationEntryAttribute = new StringArgument(null,
"getAuthorizationEntryAttribute", false, 0,
INFO_PLACEHOLDER_ATTR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get());
getAuthorizationEntryAttribute.addLongIdentifier(
"get-authorization-entry-attribute", true);
getAuthorizationEntryAttribute.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(getAuthorizationEntryAttribute);
getBackendSetID = new BooleanArgument(null, "getBackendSetID",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_BACKEND_SET_ID.get());
getBackendSetID.addLongIdentifier("get-backend-set-id", true);
getBackendSetID.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(getBackendSetID);
getServerID = new BooleanArgument(null, "getServerID",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_SERVER_ID.get());
getServerID.addLongIdentifier("get-server-id", true);
getServerID.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(getServerID);
getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get());
getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true);
getUserResourceLimits.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(getUserResourceLimits);
ignoreNoUserModification = new BooleanArgument(null,
"ignoreNoUserModification", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_IGNORE_NO_USER_MOD.get());
ignoreNoUserModification.addLongIdentifier("ignore-no-user-modification",
true);
ignoreNoUserModification.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(ignoreNoUserModification);
preReadAttribute = new StringArgument(null, "preReadAttribute", false, -1,
INFO_PLACEHOLDER_ATTR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_PRE_READ_ATTRIBUTE.get());
preReadAttribute.addLongIdentifier("preReadAttributes", true);
preReadAttribute.addLongIdentifier("pre-read-attribute", true);
preReadAttribute.addLongIdentifier("pre-read-attributes", true);
preReadAttribute.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(preReadAttribute);
postReadAttribute = new StringArgument(null, "postReadAttribute", false,
-1, INFO_PLACEHOLDER_ATTR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_POST_READ_ATTRIBUTE.get());
postReadAttribute.addLongIdentifier("postReadAttributes", true);
postReadAttribute.addLongIdentifier("post-read-attribute", true);
postReadAttribute.addLongIdentifier("post-read-attributes", true);
postReadAttribute.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(postReadAttribute);
routeToBackendSet = new StringArgument(null, "routeToBackendSet",
false, 0,
INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_BACKEND_SET.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_BACKEND_SET.get());
routeToBackendSet.addLongIdentifier("route-to-backend-set", true);
routeToBackendSet.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(routeToBackendSet);
routeToServer = new StringArgument(null, "routeToServer", false, 1,
INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_SERVER.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_SERVER.get());
routeToServer.addLongIdentifier("route-to-server", true);
routeToServer.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(routeToServer);
assuredReplication = new BooleanArgument(null, "useAssuredReplication", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPLICATION.get(
ARG_ASSURED_REPLICATION_LOCAL_LEVEL,
ARG_ASSURED_REPLICATION_REMOTE_LEVEL,
ARG_ASSURED_REPLICATION_TIMEOUT));
assuredReplication.addLongIdentifier("assuredReplication", true);
assuredReplication.addLongIdentifier("use-assured-replication", true);
assuredReplication.addLongIdentifier("assured-replication", true);
assuredReplication.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(assuredReplication);
final Set assuredReplicationLocalLevelAllowedValues =
StaticUtils.setOf("none", "received-any-server",
"processed-all-servers");
assuredReplicationLocalLevel = new StringArgument(null,
ARG_ASSURED_REPLICATION_LOCAL_LEVEL, false, 1,
INFO_PLACEHOLDER_LEVEL.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_LOCAL_LEVEL.get(
assuredReplication.getIdentifierString()),
assuredReplicationLocalLevelAllowedValues);
assuredReplicationLocalLevel.addLongIdentifier(
"assured-replication-local-level", true);
assuredReplicationLocalLevel.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(assuredReplicationLocalLevel);
final Set assuredReplicationRemoteLevelAllowedValues =
StaticUtils.setOf("none", "received-any-remote-location",
"received-all-remote-locations", "processed-all-remote-servers");
assuredReplicationRemoteLevel = new StringArgument(null,
ARG_ASSURED_REPLICATION_REMOTE_LEVEL, false, 1,
INFO_PLACEHOLDER_LEVEL.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_REMOTE_LEVEL.get(
assuredReplication.getIdentifierString()),
assuredReplicationRemoteLevelAllowedValues);
assuredReplicationRemoteLevel.addLongIdentifier(
"assured-replication-remote-level", true);
assuredReplicationRemoteLevel.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(assuredReplicationRemoteLevel);
assuredReplicationTimeout = new DurationArgument(null,
ARG_ASSURED_REPLICATION_TIMEOUT, false, INFO_PLACEHOLDER_TIMEOUT.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_TIMEOUT.get(
assuredReplication.getIdentifierString()));
assuredReplicationTimeout.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(assuredReplicationTimeout);
replicationRepair = new BooleanArgument(null, "replicationRepair",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_REPLICATION_REPAIR.get());
replicationRepair.addLongIdentifier("replication-repair", true);
replicationRepair.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(replicationRepair);
nameWithEntryUUID = new BooleanArgument(null, "nameWithEntryUUID", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_NAME_WITH_ENTRY_UUID.get());
nameWithEntryUUID.addLongIdentifier("name-with-entryUUID", true);
nameWithEntryUUID.addLongIdentifier("name-with-entry-uuid", true);
nameWithEntryUUID.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(nameWithEntryUUID);
noOperation = new BooleanArgument(null, "noOperation", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_NO_OPERATION.get());
noOperation.addLongIdentifier("noOp", true);
noOperation.addLongIdentifier("no-operation", true);
noOperation.addLongIdentifier("no-op", true);
noOperation.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(noOperation);
passwordUpdateBehavior = new StringArgument(null,
"passwordUpdateBehavior", false, 0,
INFO_LDAPMODIFY_PLACEHOLDER_NAME_EQUALS_VALUE.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_PW_UPDATE_BEHAVIOR.get());
passwordUpdateBehavior.addLongIdentifier("password-update-behavior", true);
passwordUpdateBehavior.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(passwordUpdateBehavior);
passwordValidationDetails = new BooleanArgument(null,
"getPasswordValidationDetails", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_VALIDATION_DETAILS.get(
ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD));
passwordValidationDetails.addLongIdentifier("passwordValidationDetails",
true);
passwordValidationDetails.addLongIdentifier(
"get-password-validation-details", true);
passwordValidationDetails.addLongIdentifier("password-validation-details",
true);
passwordValidationDetails.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(passwordValidationDetails);
permissiveModify = new BooleanArgument(null, "permissiveModify",
1, INFO_LDAPMODIFY_ARG_DESCRIPTION_PERMISSIVE_MODIFY.get());
permissiveModify.addLongIdentifier("permissive-modify", true);
permissiveModify.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(permissiveModify);
clientSideSubtreeDelete = new BooleanArgument(null,
"clientSideSubtreeDelete", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_CLIENT_SIDE_SUBTREE_DELETE.get());
clientSideSubtreeDelete.addLongIdentifier("client-side-subtree-delete",
true);
clientSideSubtreeDelete.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(clientSideSubtreeDelete);
serverSideSubtreeDelete = new BooleanArgument(null,
"serverSideSubtreeDelete", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_SERVER_SIDE_SUBTREE_DELETE.get());
serverSideSubtreeDelete.addLongIdentifier("server-side-subtree-delete",
true);
serverSideSubtreeDelete.addLongIdentifier("subtreeDelete", true);
serverSideSubtreeDelete.addLongIdentifier("subtree-delete", true);
serverSideSubtreeDelete.addLongIdentifier("subtreeDeleteControl", true);
serverSideSubtreeDelete.addLongIdentifier("subtree-delete-control", true);
serverSideSubtreeDelete.addLongIdentifier("useSubtreeDeleteControl", true);
serverSideSubtreeDelete.addLongIdentifier("use-subtree-delete-control",
true);
serverSideSubtreeDelete.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(serverSideSubtreeDelete);
softDelete = new BooleanArgument('s', "softDelete", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_SOFT_DELETE.get());
softDelete.addLongIdentifier("useSoftDelete", true);
softDelete.addLongIdentifier("soft-delete", true);
softDelete.addLongIdentifier("use-soft-delete", true);
softDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(softDelete);
hardDelete = new BooleanArgument(null, "hardDelete", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_HARD_DELETE.get());
hardDelete.addLongIdentifier("hard-delete", true);
hardDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(hardDelete);
allowUndelete = new BooleanArgument(null, "allowUndelete", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_ALLOW_UNDELETE.get(
ATTR_UNDELETE_FROM_DN));
allowUndelete.addLongIdentifier("allow-undelete", true);
allowUndelete.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(allowUndelete);
retireCurrentPassword = new BooleanArgument(null, "retireCurrentPassword",
1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_RETIRE_CURRENT_PASSWORD.get(
ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD));
retireCurrentPassword.addLongIdentifier("retire-current-password", true);
retireCurrentPassword.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(retireCurrentPassword);
purgeCurrentPassword = new BooleanArgument(null, "purgeCurrentPassword", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_PURGE_CURRENT_PASSWORD.get(
ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD));
purgeCurrentPassword.addLongIdentifier("purge-current-password", true);
purgeCurrentPassword.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(purgeCurrentPassword);
final Set suppressOperationalAttributeUpdatesAllowedValues =
StaticUtils.setOf("last-access-time", "last-login-time",
"last-login-ip", "lastmod");
suppressOperationalAttributeUpdates = new StringArgument(null,
"suppressOperationalAttributeUpdates", false, -1,
INFO_PLACEHOLDER_ATTR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_OP_ATTR_UPDATES.get(),
suppressOperationalAttributeUpdatesAllowedValues);
suppressOperationalAttributeUpdates.addLongIdentifier(
"suppress-operational-attribute-updates", true);
suppressOperationalAttributeUpdates.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(suppressOperationalAttributeUpdates);
suppressReferentialIntegrityUpdates = new BooleanArgument(null,
"suppressReferentialIntegrityUpdates", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_REFERINT_UPDATES.get());
suppressReferentialIntegrityUpdates.addLongIdentifier(
"suppress-referential-integrity-updates", true);
suppressReferentialIntegrityUpdates.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(suppressReferentialIntegrityUpdates);
usePasswordPolicyControl = new BooleanArgument(null,
"usePasswordPolicyControl", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_POLICY.get());
usePasswordPolicyControl.addLongIdentifier("use-password-policy-control",
true);
usePasswordPolicyControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(usePasswordPolicyControl);
uniquenessAttribute = new StringArgument(null, "uniquenessAttribute", false,
0, INFO_PLACEHOLDER_ATTR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_ATTR.get());
uniquenessAttribute.addLongIdentifier("uniquenessAttributeType", true);
uniquenessAttribute.addLongIdentifier("uniqueAttribute", true);
uniquenessAttribute.addLongIdentifier("uniqueAttributeType", true);
uniquenessAttribute.addLongIdentifier("uniqueness-attribute", true);
uniquenessAttribute.addLongIdentifier("uniqueness-attribute-type", true);
uniquenessAttribute.addLongIdentifier("unique-attribute", true);
uniquenessAttribute.addLongIdentifier("unique-attribute-type", true);
uniquenessAttribute.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessAttribute);
uniquenessFilter = new FilterArgument(null, "uniquenessFilter", false, 1,
null, INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_FILTER.get());
uniquenessFilter.addLongIdentifier("uniqueness-filter", true);
uniquenessFilter.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessFilter);
uniquenessBaseDN = new DNArgument(null, "uniquenessBaseDN", false, 1, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_BASE_DN.get());
uniquenessBaseDN.addLongIdentifier("uniqueness-base-dn", true);
uniquenessBaseDN.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessBaseDN);
parser.addDependentArgumentSet(uniquenessBaseDN, uniquenessAttribute,
uniquenessFilter);
final Set mabValues = StaticUtils.setOf(
"unique-within-each-attribute",
"unique-across-all-attributes-including-in-same-entry",
"unique-across-all-attributes-except-in-same-entry",
"unique-in-combination");
uniquenessMultipleAttributeBehavior = new StringArgument(null,
"uniquenessMultipleAttributeBehavior", false, 1,
INFO_LDAPMODIFY_PLACEHOLDER_BEHAVIOR.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_MULTIPLE_ATTRIBUTE_BEHAVIOR.
get(),
mabValues);
uniquenessMultipleAttributeBehavior.addLongIdentifier(
"uniqueness-multiple-attribute-behavior", true);
uniquenessMultipleAttributeBehavior.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessMultipleAttributeBehavior);
parser.addDependentArgumentSet(uniquenessMultipleAttributeBehavior,
uniquenessAttribute);
final Set vlValues = StaticUtils.setOf("none", "all-subtree-views",
"all-backend-sets", "all-available-backend-servers");
uniquenessPreCommitValidationLevel = new StringArgument(null,
"uniquenessPreCommitValidationLevel", false, 1,
INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_PRE_COMMIT_LEVEL.get(),
vlValues);
uniquenessPreCommitValidationLevel.addLongIdentifier(
"uniqueness-pre-commit-validation-level", true);
uniquenessPreCommitValidationLevel.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessPreCommitValidationLevel);
parser.addDependentArgumentSet(uniquenessPreCommitValidationLevel,
uniquenessAttribute, uniquenessFilter);
uniquenessPostCommitValidationLevel = new StringArgument(null,
"uniquenessPostCommitValidationLevel", false, 1,
INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_POST_COMMIT_LEVEL.get(),
vlValues);
uniquenessPostCommitValidationLevel.addLongIdentifier(
"uniqueness-post-commit-validation-level", true);
uniquenessPostCommitValidationLevel.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(uniquenessPostCommitValidationLevel);
parser.addDependentArgumentSet(uniquenessPostCommitValidationLevel,
uniquenessAttribute, uniquenessFilter);
operationControl = new ControlArgument('J', "control", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_OP_CONTROL.get());
operationControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(operationControl);
addControl = new ControlArgument(null, "addControl", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_ADD_CONTROL.get());
addControl.addLongIdentifier("add-control", true);
addControl.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(addControl);
bindControl = new ControlArgument(null, "bindControl", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_BIND_CONTROL.get());
bindControl.addLongIdentifier("bind-control", true);
bindControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(bindControl);
deleteControl = new ControlArgument(null, "deleteControl", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_DELETE_CONTROL.get());
deleteControl.addLongIdentifier("delete-control", true);
deleteControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(deleteControl);
modifyControl = new ControlArgument(null, "modifyControl", false, 0, null,
INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_CONTROL.get());
modifyControl.addLongIdentifier("modify-control", true);
modifyControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(modifyControl);
modifyDNControl = new ControlArgument(null, "modifyDNControl", false, 0,
null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_CONTROL.get());
modifyDNControl.addLongIdentifier("modify-dn-control", true);
modifyDNControl.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get());
parser.addArgument(modifyDNControl);
ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1,
INFO_PLACEHOLDER_NUM.get(),
INFO_LDAPMODIFY_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1,
Integer.MAX_VALUE);
ratePerSecond.addLongIdentifier("rate-per-second", true);
ratePerSecond.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get());
parser.addArgument(ratePerSecond);
// The "--scriptFriendly" argument is provided for compatibility with legacy
// ldapmodify tools, but is not actually used by this tool.
final BooleanArgument scriptFriendly = new BooleanArgument(null,
"scriptFriendly", 1,
INFO_LDAPMODIFY_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get());
scriptFriendly.addLongIdentifier("script-friendly", true);
scriptFriendly.setArgumentGroupName(
INFO_LDAPMODIFY_ARG_GROUP_DATA.get());
scriptFriendly.setHidden(true);
parser.addArgument(scriptFriendly);
// The "-V" / "--ldapVersion" argument is provided for compatibility with
// legacy ldapmodify tools, but is not actually used by this tool.
final IntegerArgument ldapVersion = new IntegerArgument('V', "ldapVersion",
false, 1, null, INFO_LDAPMODIFY_ARG_DESCRIPTION_LDAP_VERSION.get());
ldapVersion.addLongIdentifier("ldap-version", true);
ldapVersion.setHidden(true);
parser.addArgument(ldapVersion);
// A few assured replication arguments will only be allowed if assured
// replication is to be used.
parser.addDependentArgumentSet(assuredReplicationLocalLevel,
assuredReplication);
parser.addDependentArgumentSet(assuredReplicationRemoteLevel,
assuredReplication);
parser.addDependentArgumentSet(assuredReplicationTimeout,
assuredReplication);
// Transactions will be incompatible with a lot of settings.
parser.addExclusiveArgumentSet(useTransaction, multiUpdateErrorBehavior);
parser.addExclusiveArgumentSet(useTransaction, rejectFile);
parser.addExclusiveArgumentSet(useTransaction, retryFailedOperations);
parser.addExclusiveArgumentSet(useTransaction, continueOnError);
parser.addExclusiveArgumentSet(useTransaction, dryRun);
parser.addExclusiveArgumentSet(useTransaction, followReferrals);
parser.addExclusiveArgumentSet(useTransaction, nameWithEntryUUID);
parser.addExclusiveArgumentSet(useTransaction, noOperation);
parser.addExclusiveArgumentSet(useTransaction, modifyEntriesMatchingFilter);
parser.addExclusiveArgumentSet(useTransaction,
modifyEntriesMatchingFiltersFromFile);
parser.addExclusiveArgumentSet(useTransaction, modifyEntryWithDN);
parser.addExclusiveArgumentSet(useTransaction,
modifyEntriesWithDNsFromFile);
parser.addExclusiveArgumentSet(useTransaction,
clientSideSubtreeDelete);
// Multi-update is incompatible with a lot of settings.
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, ratePerSecond);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, rejectFile);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior,
retryFailedOperations);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, continueOnError);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, dryRun);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, followReferrals);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, nameWithEntryUUID);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, noOperation);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior,
modifyEntriesMatchingFilter);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior,
modifyEntriesMatchingFiltersFromFile);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, modifyEntryWithDN);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior,
modifyEntriesWithDNsFromFile);
parser.addExclusiveArgumentSet(multiUpdateErrorBehavior,
clientSideSubtreeDelete);
// Client-side and server-side subtree deletes cannot be used together.
parser.addExclusiveArgumentSet(clientSideSubtreeDelete,
serverSideSubtreeDelete);
// Soft delete cannot be used with either hard delete or subtree delete.
parser.addExclusiveArgumentSet(softDelete, hardDelete);
parser.addExclusiveArgumentSet(softDelete, clientSideSubtreeDelete);
parser.addExclusiveArgumentSet(softDelete, serverSideSubtreeDelete);
// Client-side subtree delete cannot be used in conjunction with a few
// other settings.
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, followReferrals);
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, preReadAttribute);
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getBackendSetID);
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getServerID);
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, noOperation);
parser.addExclusiveArgumentSet(clientSideSubtreeDelete, dryRun);
// Password retiring and purging can't be used together.
parser.addExclusiveArgumentSet(retireCurrentPassword, purgeCurrentPassword);
// Referral following cannot be used in conjunction with the manageDsaIT
// control.
parser.addExclusiveArgumentSet(followReferrals, manageDsaIT);
// The proxyAs and proxyV1As arguments cannot be used together.
parser.addExclusiveArgumentSet(proxyAs, proxyV1As);
// The modifyEntriesMatchingFilter argument is incompatible with a lot of
// settings, since it can only be used for modify operations.
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, allowUndelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, defaultAdd);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, dryRun);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, hardDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
ignoreNoUserModification);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
nameWithEntryUUID);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, softDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
clientSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
serverSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
suppressReferentialIntegrityUpdates);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, addControl);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, deleteControl);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter,
modifyDNControl);
// The modifyEntriesMatchingFilterFromFile argument is incompatible with a
// lot of settings, since it can only be used for modify operations.
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
allowUndelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
defaultAdd);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
dryRun);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
hardDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
ignoreNoUserModification);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
nameWithEntryUUID);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
softDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
clientSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
serverSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
suppressReferentialIntegrityUpdates);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
addControl);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
deleteControl);
parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile,
modifyDNControl);
// The modifyEntryWithDN argument is incompatible with a lot of
// settings, since it can only be used for modify operations.
parser.addExclusiveArgumentSet(modifyEntryWithDN, allowUndelete);
parser.addExclusiveArgumentSet(modifyEntryWithDN, defaultAdd);
parser.addExclusiveArgumentSet(modifyEntryWithDN, dryRun);
parser.addExclusiveArgumentSet(modifyEntryWithDN, hardDelete);
parser.addExclusiveArgumentSet(modifyEntryWithDN, ignoreNoUserModification);
parser.addExclusiveArgumentSet(modifyEntryWithDN, nameWithEntryUUID);
parser.addExclusiveArgumentSet(modifyEntryWithDN, softDelete);
parser.addExclusiveArgumentSet(modifyEntryWithDN, clientSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntryWithDN, serverSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntryWithDN,
suppressReferentialIntegrityUpdates);
parser.addExclusiveArgumentSet(modifyEntryWithDN, addControl);
parser.addExclusiveArgumentSet(modifyEntryWithDN, deleteControl);
parser.addExclusiveArgumentSet(modifyEntryWithDN, modifyDNControl);
// The modifyEntriesWithDNsFromFile argument is incompatible with a lot of
// settings, since it can only be used for modify operations.
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, allowUndelete);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, defaultAdd);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, dryRun);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, hardDelete);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
ignoreNoUserModification);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
nameWithEntryUUID);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, softDelete);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
clientSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
serverSideSubtreeDelete);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
suppressReferentialIntegrityUpdates);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, addControl);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, deleteControl);
parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile,
modifyDNControl);
}
/**
* {@inheritDoc}
*/
@Override()
public void doExtendedNonLDAPArgumentValidation()
throws ArgumentException
{
// If we should use the route to backend set request control, then validate
// and pre-create those controls.
if (routeToBackendSet.isPresent())
{
final List values = routeToBackendSet.getValues();
final Map> idsByRP = new LinkedHashMap<>(
StaticUtils.computeMapCapacity(values.size()));
for (final String value : values)
{
final int colonPos = value.indexOf(':');
if (colonPos <= 0)
{
throw new ArgumentException(
ERR_LDAPMODIFY_ROUTE_TO_BACKEND_SET_INVALID_FORMAT.get(value,
routeToBackendSet.getIdentifierString()));
}
final String rpID = value.substring(0, colonPos);
final String bsID = value.substring(colonPos+1);
List idsForRP = idsByRP.get(rpID);
if (idsForRP == null)
{
idsForRP = new ArrayList<>(values.size());
idsByRP.put(rpID, idsForRP);
}
idsForRP.add(bsID);
}
for (final Map.Entry> e : idsByRP.entrySet())
{
final String rpID = e.getKey();
final List bsIDs = e.getValue();
routeToBackendSetRequestControls.add(
RouteToBackendSetRequestControl.createAbsoluteRoutingRequest(true,
rpID, bsIDs));
}
}
}
/**
* {@inheritDoc}
*/
@Override()
protected List getBindControls()
{
final ArrayList bindControls = new ArrayList<>(10);
if (bindControl.isPresent())
{
bindControls.addAll(bindControl.getValues());
}
if (authorizationIdentity.isPresent())
{
bindControls.add(new AuthorizationIdentityRequestControl(false));
}
if (getAuthorizationEntryAttribute.isPresent())
{
bindControls.add(new GetAuthorizationEntryRequestControl(true, true,
getAuthorizationEntryAttribute.getValues()));
}
if (getUserResourceLimits.isPresent())
{
bindControls.add(new GetUserResourceLimitsRequestControl());
}
if (usePasswordPolicyControl.isPresent())
{
bindControls.add(new PasswordPolicyRequestControl());
}
if (suppressOperationalAttributeUpdates.isPresent())
{
final EnumSet suppressTypes =
EnumSet.noneOf(SuppressType.class);
for (final String s : suppressOperationalAttributeUpdates.getValues())
{
if (s.equalsIgnoreCase("last-access-time"))
{
suppressTypes.add(SuppressType.LAST_ACCESS_TIME);
}
else if (s.equalsIgnoreCase("last-login-time"))
{
suppressTypes.add(SuppressType.LAST_LOGIN_TIME);
}
else if (s.equalsIgnoreCase("last-login-ip"))
{
suppressTypes.add(SuppressType.LAST_LOGIN_IP);
}
}
bindControls.add(new SuppressOperationalAttributeUpdateRequestControl(
suppressTypes));
}
return bindControls;
}
/**
* {@inheritDoc}
*/
@Override()
protected boolean supportsMultipleServers()
{
// We will support providing information about multiple servers. This tool
// will not communicate with multiple servers concurrently, but it can
// accept information about multiple servers in the event that a large set
// of changes is to be processed and a server goes down in the middle of
// those changes. In this case, we can resume processing on a newly-created
// connection, possibly to a different server.
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public LDAPConnectionOptions getConnectionOptions()
{
final LDAPConnectionOptions options = new LDAPConnectionOptions();
options.setUseSynchronousMode(true);
options.setFollowReferrals(followReferrals.isPresent());
options.setUnsolicitedNotificationHandler(this);
options.setResponseTimeoutMillis(0L);
return options;
}
/**
* {@inheritDoc}
*/
@Override()
public ResultCode doToolProcessing()
{
// Examine the arguments to determine the sets of controls to use for each
// type of request.
final ArrayList addControls = new ArrayList<>(10);
final ArrayList deleteControls = new ArrayList<>(10);
final ArrayList modifyControls = new ArrayList<>(10);
final ArrayList modifyDNControls = new ArrayList<>(10);
final ArrayList searchControls = new ArrayList<>(10);
try
{
createRequestControls(addControls, deleteControls, modifyControls,
modifyDNControls, searchControls);
}
catch (final LDAPException le)
{
Debug.debugException(le);
for (final String line :
ResultUtils.formatResult(le, true, 0, WRAP_COLUMN))
{
err(line);
}
return le.getResultCode();
}
// If an encryption passphrase file was specified, then read its value.
String encryptionPassphrase = null;
if (encryptionPassphraseFile.isPresent())
{
try
{
encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile(
encryptionPassphraseFile.getValue());
}
catch (final LDAPException e)
{
Debug.debugException(e);
wrapErr(0, WRAP_COLUMN, e.getMessage());
return e.getResultCode();
}
}
LDAPConnectionPool connectionPool = null;
LDIFReader ldifReader = null;
LDIFWriter rejectWriter = null;
try
{
// Create a connection pool that will be used to communicate with the
// directory server. If we should use an administrative session, then
// create a connect processor that will be used to start the session
// before performing the bind.
try
{
final StartAdministrativeSessionPostConnectProcessor p;
if (useAdministrativeSession.isPresent())
{
p = new StartAdministrativeSessionPostConnectProcessor(
new StartAdministrativeSessionExtendedRequest(getToolName(),
true));
}
else
{
p = null;
}
if (! dryRun.isPresent())
{
connectionPool = getConnectionPool(1, 2, 0, p, null, true,
new ReportBindResultLDAPConnectionPoolHealthCheck(this, true,
verbose.isPresent()));
}
}
catch (final LDAPException le)
{
Debug.debugException(le);
// Unable to create the connection pool, which means that either the
// connection could not be established or the attempt to authenticate
// the connection failed. If the bind failed, then the report bind
// result health check should have already reported the bind failure.
// If the failure was something else, then display that failure result.
if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS)
{
for (final String line :
ResultUtils.formatResult(le, true, 0, WRAP_COLUMN))
{
err(line);
}
}
return le.getResultCode();
}
if ((connectionPool != null) && retryFailedOperations.isPresent())
{
connectionPool.setRetryFailedOperationsDueToInvalidConnections(true);
}
// Report that the connection was successfully established.
if (connectionPool != null)
{
try
{
final LDAPConnection connection = connectionPool.getConnection();
final String hostPort = connection.getHostPort();
connectionPool.releaseConnection(connection);
commentToOut(INFO_LDAPMODIFY_CONNECTION_ESTABLISHED.get(hostPort));
out();
}
catch (final LDAPException le)
{
Debug.debugException(le);
// This should never happen.
}
}
// If we should process the operations in a transaction, then start that
// now.
final ASN1OctetString txnID;
if (useTransaction.isPresent())
{
final Control[] startTxnControls;
if (proxyAs.isPresent())
{
// In a transaction, the proxied authorization control must only be
// used in the start transaction request and not in any of the
// subsequent operation requests.
startTxnControls = new Control[]
{
new ProxiedAuthorizationV2RequestControl(proxyAs.getValue())
};
}
else if (proxyV1As.isPresent())
{
// In a transaction, the proxied authorization control must only be
// used in the start transaction request and not in any of the
// subsequent operation requests.
startTxnControls = new Control[]
{
new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue())
};
}
else
{
startTxnControls = StaticUtils.NO_CONTROLS;
}
try
{
final StartTransactionExtendedResult startTxnResult =
(StartTransactionExtendedResult)
connectionPool.processExtendedOperation(
new StartTransactionExtendedRequest(startTxnControls));
if (startTxnResult.getResultCode() == ResultCode.SUCCESS)
{
txnID = startTxnResult.getTransactionID();
final TransactionSpecificationRequestControl c =
new TransactionSpecificationRequestControl(txnID);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
final String txnIDString;
if (StaticUtils.isPrintableString(txnID.getValue()))
{
txnIDString = txnID.stringValue();
}
else
{
final StringBuilder hexBuffer = new StringBuilder();
StaticUtils.toHex(txnID.getValue(), ":", hexBuffer);
txnIDString = hexBuffer.toString();
}
commentToOut(INFO_LDAPMODIFY_STARTED_TXN.get(txnIDString));
}
else
{
commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get(
startTxnResult.getResultString()));
return startTxnResult.getResultCode();
}
}
catch (final LDAPException le)
{
Debug.debugException(le);
commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get(
StaticUtils.getExceptionMessage(le)));
return le.getResultCode();
}
}
else
{
txnID = null;
}
// Create an LDIF reader that will be used to read the changes to process.
try
{
final InputStream ldifInputStream;
if (ldifFile.isPresent())
{
ldifInputStream = ToolUtils.getInputStreamForLDIFFiles(
ldifFile.getValues(), encryptionPassphrase, getOut(),
getErr()).getFirst();
}
else
{
ldifInputStream = in;
}
ldifReader = new LDIFReader(ldifInputStream, 0, null, null,
characterSet.getValue());
}
catch (final Exception e)
{
commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_LDIF_READER.get(
StaticUtils.getExceptionMessage(e)));
return ResultCode.LOCAL_ERROR;
}
if (stripTrailingSpaces.isPresent())
{
ldifReader.setTrailingSpaceBehavior(TrailingSpaceBehavior.STRIP);
}
// If appropriate, create a reject writer.
if (rejectFile.isPresent())
{
try
{
rejectWriter = new LDIFWriter(rejectFile.getValue());
// Set the maximum allowed wrap column. This is better than setting a
// wrap column of zero because it will ensure that comments don't get
// wrapped either.
rejectWriter.setWrapColumn(Integer.MAX_VALUE);
}
catch (final Exception e)
{
Debug.debugException(e);
commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_REJECT_WRITER.get(
rejectFile.getValue().getAbsolutePath(),
StaticUtils.getExceptionMessage(e)));
return ResultCode.LOCAL_ERROR;
}
}
// If appropriate, create a rate limiter.
final FixedRateBarrier rateLimiter;
if (ratePerSecond.isPresent())
{
rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue());
}
else
{
rateLimiter = null;
}
// Iterate through the set of changes to process.
boolean commitTransaction = true;
ResultCode resultCode = null;
final ArrayList multiUpdateRequests =
new ArrayList<>(10);
final boolean isBulkModify = modifyEntriesMatchingFilter.isPresent() ||
modifyEntriesMatchingFiltersFromFile.isPresent() ||
modifyEntryWithDN.isPresent() ||
modifyEntriesWithDNsFromFile.isPresent();
readChangeRecordLoop:
while (true)
{
// If there is a rate limiter, then use it to sleep if necessary.
if ((rateLimiter != null) && (! isBulkModify))
{
rateLimiter.await();
}
// Read the next LDIF change record. If we get an error then handle it
// and abort if appropriate.
final LDIFChangeRecord changeRecord;
try
{
changeRecord = ldifReader.readChangeRecord(defaultAdd.isPresent());
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
final String message = ERR_LDAPMODIFY_IO_ERROR_READING_CHANGE.get(
StaticUtils.getExceptionMessage(ioe));
commentToErr(message);
writeRejectedChange(rejectWriter, message, null);
commitTransaction = false;
resultCode = ResultCode.LOCAL_ERROR;
break;
}
catch (final LDIFException le)
{
Debug.debugException(le);
final StringBuilder buffer = new StringBuilder();
if (le.mayContinueReading() && (! useTransaction.isPresent()))
{
buffer.append(
ERR_LDAPMODIFY_RECOVERABLE_LDIF_ERROR_READING_CHANGE.get(
le.getLineNumber(), StaticUtils.getExceptionMessage(le)));
}
else
{
buffer.append(
ERR_LDAPMODIFY_UNRECOVERABLE_LDIF_ERROR_READING_CHANGE.get(
le.getLineNumber(), StaticUtils.getExceptionMessage(le)));
}
if ((resultCode == null) || (resultCode == ResultCode.SUCCESS))
{
resultCode = ResultCode.LOCAL_ERROR;
}
if ((le.getDataLines() != null) && (! le.getDataLines().isEmpty()))
{
buffer.append(StaticUtils.EOL);
buffer.append(StaticUtils.EOL);
buffer.append(ERR_LDAPMODIFY_INVALID_LINES.get());
buffer.append(StaticUtils.EOL);
for (final String s : le.getDataLines())
{
buffer.append(s);
buffer.append(StaticUtils.EOL);
}
}
final String message = buffer.toString();
commentToErr(message);
writeRejectedChange(rejectWriter, message, null);
if (le.mayContinueReading() && (! useTransaction.isPresent()))
{
continue;
}
else
{
commitTransaction = false;
resultCode = ResultCode.LOCAL_ERROR;
break;
}
}
// If we read a null change record, then there are no more changes to
// process. Otherwise, treat it appropriately based on the operation
// type.
if (changeRecord == null)
{
break;
}
// If we should modify entries matching a specified filter, then convert
// the change record into a set of modifications.
if (modifyEntriesMatchingFilter.isPresent())
{
for (final Filter filter : modifyEntriesMatchingFilter.getValues())
{
final ResultCode rc = handleModifyMatchingFilter(connectionPool,
changeRecord,
modifyEntriesMatchingFilter.getIdentifierString(),
filter, searchControls, modifyControls, rateLimiter,
rejectWriter);
if (rc != ResultCode.SUCCESS)
{
if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = rc;
}
}
}
}
if (modifyEntriesMatchingFiltersFromFile.isPresent())
{
for (final File f : modifyEntriesMatchingFiltersFromFile.getValues())
{
final FilterFileReader filterReader;
try
{
filterReader = new FilterFileReader(f);
}
catch (final Exception e)
{
Debug.debugException(e);
commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_FILTER_FILE.get(
f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)));
return ResultCode.LOCAL_ERROR;
}
try
{
while (true)
{
final Filter filter;
try
{
filter = filterReader.readFilter();
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_FILTER_FILE.get(
f.getAbsolutePath(),
StaticUtils.getExceptionMessage(ioe)));
return ResultCode.LOCAL_ERROR;
}
catch (final LDAPException le)
{
Debug.debugException(le);
commentToErr(le.getMessage());
if (continueOnError.isPresent())
{
if ((resultCode == null) ||
(resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = le.getResultCode();
}
continue;
}
else
{
return le.getResultCode();
}
}
if (filter == null)
{
break;
}
final ResultCode rc = handleModifyMatchingFilter(connectionPool,
changeRecord,
modifyEntriesMatchingFiltersFromFile.getIdentifierString(),
filter, searchControls, modifyControls, rateLimiter,
rejectWriter);
if (rc != ResultCode.SUCCESS)
{
if ((resultCode == null) ||
(resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = rc;
}
}
}
}
finally
{
try
{
filterReader.close();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
}
}
if (modifyEntryWithDN.isPresent())
{
for (final DN dn : modifyEntryWithDN.getValues())
{
final ResultCode rc = handleModifyWithDN(connectionPool,
changeRecord, modifyEntryWithDN.getIdentifierString(), dn,
modifyControls, rateLimiter, rejectWriter);
if (rc != ResultCode.SUCCESS)
{
if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = rc;
}
}
}
}
if (modifyEntriesWithDNsFromFile.isPresent())
{
for (final File f : modifyEntriesWithDNsFromFile.getValues())
{
final DNFileReader dnReader;
try
{
dnReader = new DNFileReader(f);
}
catch (final Exception e)
{
Debug.debugException(e);
commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_DN_FILE.get(
f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)));
return ResultCode.LOCAL_ERROR;
}
try
{
while (true)
{
final DN dn;
try
{
dn = dnReader.readDN();
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_DN_FILE.get(
f.getAbsolutePath(),
StaticUtils.getExceptionMessage(ioe)));
return ResultCode.LOCAL_ERROR;
}
catch (final LDAPException le)
{
Debug.debugException(le);
commentToErr(le.getMessage());
if (continueOnError.isPresent())
{
if ((resultCode == null) ||
(resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = le.getResultCode();
}
continue;
}
else
{
return le.getResultCode();
}
}
if (dn == null)
{
break;
}
final ResultCode rc = handleModifyWithDN(connectionPool,
changeRecord,
modifyEntriesWithDNsFromFile.getIdentifierString(), dn,
modifyControls, rateLimiter, rejectWriter);
if (rc != ResultCode.SUCCESS)
{
if ((resultCode == null) ||
(resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = rc;
}
}
}
}
finally
{
try
{
dnReader.close();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
}
}
if (isBulkModify)
{
continue;
}
try
{
final ResultCode rc;
if (changeRecord instanceof LDIFAddChangeRecord)
{
rc = doAdd((LDIFAddChangeRecord) changeRecord, addControls,
connectionPool, multiUpdateRequests, rejectWriter);
}
else if (changeRecord instanceof LDIFDeleteChangeRecord)
{
rc = doDelete((LDIFDeleteChangeRecord) changeRecord, deleteControls,
connectionPool, multiUpdateRequests, rejectWriter);
}
else if (changeRecord instanceof LDIFModifyChangeRecord)
{
rc = doModify((LDIFModifyChangeRecord) changeRecord, modifyControls,
connectionPool, multiUpdateRequests, rejectWriter);
}
else if (changeRecord instanceof LDIFModifyDNChangeRecord)
{
rc = doModifyDN((LDIFModifyDNChangeRecord) changeRecord,
modifyDNControls, connectionPool, multiUpdateRequests,
rejectWriter);
}
else
{
// This should never happen.
commentToErr(ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get());
for (final String line : changeRecord.toLDIF())
{
err("# " + line);
}
throw new LDAPException(ResultCode.PARAM_ERROR,
ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get() +
changeRecord.toString());
}
if ((resultCode == null) && (rc != ResultCode.SUCCESS))
{
resultCode = rc;
}
}
catch (final LDAPException le)
{
Debug.debugException(le);
commitTransaction = false;
if (continueOnError.isPresent())
{
if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) ||
(resultCode == ResultCode.NO_OPERATION))
{
resultCode = le.getResultCode();
}
}
else
{
resultCode = le.getResultCode();
break;
}
}
}
// If the operations are part of a transaction, then commit or abort that
// transaction now. Otherwise, if they should be part of a multi-update
// operation, then process that now.
if (useTransaction.isPresent())
{
LDAPResult endTxnResult;
final EndTransactionExtendedRequest endTxnRequest =
new EndTransactionExtendedRequest(txnID, commitTransaction);
try
{
endTxnResult = connectionPool.processExtendedOperation(endTxnRequest);
}
catch (final LDAPException le)
{
endTxnResult = le.toLDAPResult();
}
displayResult(endTxnResult, false);
if (((resultCode == null) || (resultCode == ResultCode.SUCCESS)) &&
(endTxnResult.getResultCode() != ResultCode.SUCCESS))
{
resultCode = endTxnResult.getResultCode();
}
}
else if (multiUpdateErrorBehavior.isPresent())
{
final MultiUpdateErrorBehavior errorBehavior;
if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase("atomic"))
{
errorBehavior = MultiUpdateErrorBehavior.ATOMIC;
}
else if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase(
"abort-on-error"))
{
errorBehavior = MultiUpdateErrorBehavior.ABORT_ON_ERROR;
}
else
{
errorBehavior = MultiUpdateErrorBehavior.CONTINUE_ON_ERROR;
}
final Control[] multiUpdateControls;
if (proxyAs.isPresent())
{
multiUpdateControls = new Control[]
{
new ProxiedAuthorizationV2RequestControl(proxyAs.getValue())
};
}
else if (proxyV1As.isPresent())
{
multiUpdateControls = new Control[]
{
new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue())
};
}
else
{
multiUpdateControls = StaticUtils.NO_CONTROLS;
}
ExtendedResult multiUpdateResult;
try
{
commentToOut(INFO_LDAPMODIFY_SENDING_MULTI_UPDATE_REQUEST.get());
final MultiUpdateExtendedRequest multiUpdateRequest =
new MultiUpdateExtendedRequest(errorBehavior,
multiUpdateRequests, multiUpdateControls);
multiUpdateResult =
connectionPool.processExtendedOperation(multiUpdateRequest);
}
catch (final LDAPException le)
{
multiUpdateResult = new ExtendedResult(le);
}
displayResult(multiUpdateResult, false);
resultCode = multiUpdateResult.getResultCode();
}
if (resultCode == null)
{
return ResultCode.SUCCESS;
}
else
{
return resultCode;
}
}
finally
{
if (rejectWriter != null)
{
try
{
rejectWriter.close();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
if (ldifReader != null)
{
try
{
ldifReader.close();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
if (connectionPool != null)
{
try
{
connectionPool.close();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
}
}
/**
* Handles the processing for a change record when the tool should modify
* entries matching a given filter.
*
* @param connectionPool The connection pool to use to communicate with
* the directory server.
* @param changeRecord The LDIF change record to be processed.
* @param argIdentifierString The identifier string for the argument used to
* specify the filter to use to identify the
* entries to modify.
* @param filter The filter to use to identify the entries to
* modify.
* @param searchControls The set of controls to include in the search
* request.
* @param modifyControls The set of controls to include in the modify
* requests.
* @param rateLimiter The fixed-rate barrier to use for rate
* limiting. It may be {@code null} if no rate
* limiting is required.
* @param rejectWriter The reject writer to use to record information
* about any failed operations.
*
* @return A result code obtained from processing.
*/
private ResultCode handleModifyMatchingFilter(
final LDAPConnectionPool connectionPool,
final LDIFChangeRecord changeRecord,
final String argIdentifierString, final Filter filter,
final List searchControls,
final List modifyControls,
final FixedRateBarrier rateLimiter,
final LDIFWriter rejectWriter)
{
// If the provided change record isn't a modify change record, then that's
// an error. Reject it.
if (! (changeRecord instanceof LDIFModifyChangeRecord))
{
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString),
changeRecord);
return ResultCode.PARAM_ERROR;
}
final LDIFModifyChangeRecord modifyChangeRecord =
(LDIFModifyChangeRecord) changeRecord;
final HashSet processedDNs =
new HashSet<>(StaticUtils.computeMapCapacity(100));
// If we need to use the simple paged results control, then we may have to
// issue multiple searches.
ASN1OctetString pagedResultsCookie = null;
long entriesProcessed = 0L;
ResultCode resultCode = ResultCode.SUCCESS;
while (true)
{
// Construct the search request to send.
final LDAPModifySearchListener listener =
new LDAPModifySearchListener(this, modifyChangeRecord, filter,
modifyControls, connectionPool, rateLimiter, rejectWriter,
processedDNs);
final SearchRequest searchRequest =
new SearchRequest(listener, modifyChangeRecord.getDN(),
SearchScope.SUB, filter, SearchRequest.NO_ATTRIBUTES);
searchRequest.setControls(searchControls);
if (searchPageSize.isPresent())
{
searchRequest.addControl(new SimplePagedResultsControl(
searchPageSize.getValue(), pagedResultsCookie));
}
// The connection pool's automatic retry feature can't work for searches
// that return one or more entries before encountering a failure. To get
// around that, we'll check a connection out of the pool and use it to
// process the search. If an error occurs that indicates the connection
// is no longer valid, we can replace it with a newly-established
// connection and try again. The search result listener will ensure that
// no entry gets updated twice.
LDAPConnection connection;
try
{
connection = connectionPool.getConnection();
}
catch (final LDAPException le)
{
Debug.debugException(le);
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_CANNOT_GET_SEARCH_CONNECTION.get(
modifyChangeRecord.getDN(), String.valueOf(filter),
StaticUtils.getExceptionMessage(le)),
modifyChangeRecord, le.toLDAPResult());
return le.getResultCode();
}
SearchResult searchResult;
boolean connectionValid = false;
try
{
try
{
searchResult = connection.search(searchRequest);
}
catch (final LDAPSearchException lse)
{
searchResult = lse.getSearchResult();
}
if (searchResult.getResultCode() == ResultCode.SUCCESS)
{
connectionValid = true;
}
else if (searchResult.getResultCode().isConnectionUsable())
{
connectionValid = true;
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(),
String.valueOf(filter)),
modifyChangeRecord, searchResult);
return searchResult.getResultCode();
}
else if (retryFailedOperations.isPresent())
{
try
{
connection = connectionPool.replaceDefunctConnection(connection);
}
catch (final LDAPException le)
{
Debug.debugException(le);
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_SEARCH_FAILED_CANNOT_RECONNECT.get(
modifyChangeRecord.getDN(), String.valueOf(filter)),
modifyChangeRecord, searchResult);
return searchResult.getResultCode();
}
try
{
searchResult = connection.search(searchRequest);
}
catch (final LDAPSearchException lse)
{
Debug.debugException(lse);
searchResult = lse.getSearchResult();
}
if (searchResult.getResultCode() == ResultCode.SUCCESS)
{
connectionValid = true;
}
else
{
connectionValid = searchResult.getResultCode().isConnectionUsable();
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(),
String.valueOf(filter)),
modifyChangeRecord, searchResult);
return searchResult.getResultCode();
}
}
else
{
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(),
String.valueOf(filter)),
modifyChangeRecord, searchResult);
return searchResult.getResultCode();
}
}
finally
{
if (connectionValid)
{
connectionPool.releaseConnection(connection);
}
else
{
connectionPool.releaseDefunctConnection(connection);
}
}
// If we've gotten here, then the search was successful. Check to see if
// any of the modifications failed, and if so then update the result code
// accordingly.
if ((resultCode == ResultCode.SUCCESS) &&
(listener.getResultCode() != ResultCode.SUCCESS))
{
resultCode = listener.getResultCode();
}
// If the search used the simple paged results control then we may need to
// repeat the search to get the next page.
entriesProcessed += searchResult.getEntryCount();
if (searchPageSize.isPresent())
{
final SimplePagedResultsControl responseControl;
try
{
responseControl = SimplePagedResultsControl.get(searchResult);
}
catch (final LDAPException le)
{
Debug.debugException(le);
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_CANNOT_DECODE_PAGED_RESULTS_CONTROL.get(
modifyChangeRecord.getDN(), String.valueOf(filter)),
modifyChangeRecord, le.toLDAPResult());
return le.getResultCode();
}
if (responseControl == null)
{
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_MISSING_PAGED_RESULTS_RESPONSE.get(
modifyChangeRecord.getDN(), String.valueOf(filter)),
modifyChangeRecord);
return ResultCode.CONTROL_NOT_FOUND;
}
else
{
pagedResultsCookie = responseControl.getCookie();
if (responseControl.moreResultsToReturn())
{
if (verbose.isPresent())
{
commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED_MORE_PAGES.get(
modifyChangeRecord.getDN(), String.valueOf(filter),
entriesProcessed));
for (final String resultLine :
ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN))
{
out(resultLine);
}
out();
}
}
else
{
commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get(
entriesProcessed, modifyChangeRecord.getDN(),
String.valueOf(filter)));
if (verbose.isPresent())
{
for (final String resultLine :
ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN))
{
out(resultLine);
}
}
out();
return resultCode;
}
}
}
else
{
commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get(
entriesProcessed, modifyChangeRecord.getDN(),
String.valueOf(filter)));
if (verbose.isPresent())
{
for (final String resultLine :
ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN))
{
out(resultLine);
}
}
out();
return resultCode;
}
}
}
/**
* Handles the processing for a change record when the tool should modify an
* entry with a given DN instead of the DN contained in the change record.
*
* @param connectionPool The connection pool to use to communicate with
* the directory server.
* @param changeRecord The LDIF change record to be processed.
* @param argIdentifierString The identifier string for the argument used to
* specify the DN of the entry to modify.
* @param dn The DN of the entry to modify.
* @param modifyControls The set of controls to include in the modify
* requests.
* @param rateLimiter The fixed-rate barrier to use for rate
* limiting. It may be {@code null} if no rate
* limiting is required.
* @param rejectWriter The reject writer to use to record information
* about any failed operations.
*
* @return A result code obtained from processing.
*/
private ResultCode handleModifyWithDN(
final LDAPConnectionPool connectionPool,
final LDIFChangeRecord changeRecord,
final String argIdentifierString, final DN dn,
final List modifyControls,
final FixedRateBarrier rateLimiter,
final LDIFWriter rejectWriter)
{
// If the provided change record isn't a modify change record, then that's
// an error. Reject it.
if (! (changeRecord instanceof LDIFModifyChangeRecord))
{
writeRejectedChange(rejectWriter,
ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString),
changeRecord);
return ResultCode.PARAM_ERROR;
}
// Create a new modify change record with the provided DN instead of the
// original DN.
final LDIFModifyChangeRecord originalChangeRecord =
(LDIFModifyChangeRecord) changeRecord;
final LDIFModifyChangeRecord updatedChangeRecord =
new LDIFModifyChangeRecord(dn.toString(),
originalChangeRecord.getModifications(),
originalChangeRecord.getControls());
if (rateLimiter != null)
{
rateLimiter.await();
}
try
{
return doModify(updatedChangeRecord, modifyControls, connectionPool, null,
rejectWriter);
}
catch (final LDAPException le)
{
Debug.debugException(le);
return le.getResultCode();
}
}
/**
* Populates lists of request controls that should be included in requests
* of various types.
*
* @param addControls The list of controls to include in add requests.
* @param deleteControls The list of controls to include in delete
* requests.
* @param modifyControls The list of controls to include in modify
* requests.
* @param modifyDNControls The list of controls to include in modify DN
* requests.
* @param searchControls The list of controls to include in search
* requests.
*
* @throws LDAPException If a problem is encountered while creating any of
* the requested controls.
*/
private void createRequestControls(final List addControls,
final List deleteControls,
final List modifyControls,
final List modifyDNControls,
final List searchControls)
throws LDAPException
{
if (addControl.isPresent())
{
addControls.addAll(addControl.getValues());
}
if (deleteControl.isPresent())
{
deleteControls.addAll(deleteControl.getValues());
}
if (modifyControl.isPresent())
{
modifyControls.addAll(modifyControl.getValues());
}
if (modifyDNControl.isPresent())
{
modifyDNControls.addAll(modifyDNControl.getValues());
}
if (operationControl.isPresent())
{
addControls.addAll(operationControl.getValues());
deleteControls.addAll(operationControl.getValues());
modifyControls.addAll(operationControl.getValues());
modifyDNControls.addAll(operationControl.getValues());
}
addControls.addAll(routeToBackendSetRequestControls);
deleteControls.addAll(routeToBackendSetRequestControls);
modifyControls.addAll(routeToBackendSetRequestControls);
modifyDNControls.addAll(routeToBackendSetRequestControls);
if (noOperation.isPresent())
{
final NoOpRequestControl c = new NoOpRequestControl();
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (generatePassword.isPresent())
{
addControls.add(new GeneratePasswordRequestControl());
}
if (getBackendSetID.isPresent())
{
final GetBackendSetIDRequestControl c =
new GetBackendSetIDRequestControl(false);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (getServerID.isPresent())
{
final GetServerIDRequestControl c =
new GetServerIDRequestControl(false);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (ignoreNoUserModification.isPresent())
{
addControls.add(new IgnoreNoUserModificationRequestControl(false));
modifyControls.add(new IgnoreNoUserModificationRequestControl(false));
}
if (nameWithEntryUUID.isPresent())
{
addControls.add(new NameWithEntryUUIDRequestControl(true));
}
if (permissiveModify.isPresent())
{
modifyControls.add(new PermissiveModifyRequestControl(false));
}
if (routeToServer.isPresent())
{
final RouteToServerRequestControl c =
new RouteToServerRequestControl(false,
routeToServer.getValue(), false, false, false);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (suppressReferentialIntegrityUpdates.isPresent())
{
final SuppressReferentialIntegrityUpdatesRequestControl c =
new SuppressReferentialIntegrityUpdatesRequestControl(true);
deleteControls.add(c);
modifyDNControls.add(c);
}
if (suppressOperationalAttributeUpdates.isPresent())
{
final EnumSet suppressTypes =
EnumSet.noneOf(SuppressType.class);
for (final String s : suppressOperationalAttributeUpdates.getValues())
{
if (s.equalsIgnoreCase("last-access-time"))
{
suppressTypes.add(SuppressType.LAST_ACCESS_TIME);
}
else if (s.equalsIgnoreCase("last-login-time"))
{
suppressTypes.add(SuppressType.LAST_LOGIN_TIME);
}
else if (s.equalsIgnoreCase("last-login-ip"))
{
suppressTypes.add(SuppressType.LAST_LOGIN_IP);
}
else if (s.equalsIgnoreCase("lastmod"))
{
suppressTypes.add(SuppressType.LASTMOD);
}
}
final SuppressOperationalAttributeUpdateRequestControl c =
new SuppressOperationalAttributeUpdateRequestControl(suppressTypes);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (usePasswordPolicyControl.isPresent())
{
final PasswordPolicyRequestControl c = new PasswordPolicyRequestControl();
addControls.add(c);
modifyControls.add(c);
}
if (assuredReplication.isPresent())
{
AssuredReplicationLocalLevel localLevel = null;
if (assuredReplicationLocalLevel.isPresent())
{
final String level = assuredReplicationLocalLevel.getValue();
if (level.equalsIgnoreCase("none"))
{
localLevel = AssuredReplicationLocalLevel.NONE;
}
else if (level.equalsIgnoreCase("received-any-server"))
{
localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER;
}
else if (level.equalsIgnoreCase("processed-all-servers"))
{
localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS;
}
}
AssuredReplicationRemoteLevel remoteLevel = null;
if (assuredReplicationRemoteLevel.isPresent())
{
final String level = assuredReplicationRemoteLevel.getValue();
if (level.equalsIgnoreCase("none"))
{
remoteLevel = AssuredReplicationRemoteLevel.NONE;
}
else if (level.equalsIgnoreCase("received-any-remote-location"))
{
remoteLevel =
AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION;
}
else if (level.equalsIgnoreCase("received-all-remote-locations"))
{
remoteLevel =
AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS;
}
else if (level.equalsIgnoreCase("processed-all-remote-servers"))
{
remoteLevel =
AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS;
}
}
Long timeoutMillis = null;
if (assuredReplicationTimeout.isPresent())
{
timeoutMillis =
assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS);
}
final AssuredReplicationRequestControl c =
new AssuredReplicationRequestControl(true, localLevel, localLevel,
remoteLevel, remoteLevel, timeoutMillis, false);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (hardDelete.isPresent() && (! clientSideSubtreeDelete.isPresent()))
{
deleteControls.add(new HardDeleteRequestControl(true));
}
if (replicationRepair.isPresent())
{
final ReplicationRepairRequestControl c =
new ReplicationRepairRequestControl();
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (softDelete.isPresent())
{
deleteControls.add(new SoftDeleteRequestControl(true, true));
}
if (serverSideSubtreeDelete.isPresent())
{
deleteControls.add(new SubtreeDeleteRequestControl());
}
if (assertionFilter.isPresent())
{
final AssertionRequestControl c = new AssertionRequestControl(
assertionFilter.getValue(), true);
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (operationPurpose.isPresent())
{
final OperationPurposeRequestControl c =
new OperationPurposeRequestControl(false, "ldapmodify",
Version.NUMERIC_VERSION_STRING,
LDAPModify.class.getName() + ".createRequestControls",
operationPurpose.getValue());
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (manageDsaIT.isPresent())
{
final ManageDsaITRequestControl c = new ManageDsaITRequestControl(true);
addControls.add(c);
if (! clientSideSubtreeDelete.isPresent())
{
deleteControls.add(c);
}
modifyControls.add(c);
modifyDNControls.add(c);
}
if (passwordUpdateBehavior.isPresent())
{
final PasswordUpdateBehaviorRequestControl c =
createPasswordUpdateBehaviorRequestControl(
passwordUpdateBehavior.getIdentifierString(),
passwordUpdateBehavior.getValues());
addControls.add(c);
modifyControls.add(c);
}
if (preReadAttribute.isPresent())
{
final ArrayList attrList = new ArrayList<>(10);
for (final String value : preReadAttribute.getValues())
{
final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
while (tokenizer.hasMoreTokens())
{
attrList.add(tokenizer.nextToken());
}
}
final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS);
final PreReadRequestControl c = new PreReadRequestControl(attrArray);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (postReadAttribute.isPresent())
{
final ArrayList attrList = new ArrayList<>(10);
for (final String value : postReadAttribute.getValues())
{
final StringTokenizer tokenizer = new StringTokenizer(value, ", ");
while (tokenizer.hasMoreTokens())
{
attrList.add(tokenizer.nextToken());
}
}
final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS);
final PostReadRequestControl c = new PostReadRequestControl(attrArray);
addControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
if (proxyAs.isPresent() && (! useTransaction.isPresent()) &&
(! multiUpdateErrorBehavior.isPresent()))
{
final ProxiedAuthorizationV2RequestControl c =
new ProxiedAuthorizationV2RequestControl(proxyAs.getValue());
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
searchControls.add(c);
}
if (proxyV1As.isPresent() && (! useTransaction.isPresent()) &&
(! multiUpdateErrorBehavior.isPresent()))
{
final ProxiedAuthorizationV1RequestControl c =
new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue());
addControls.add(c);
deleteControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
searchControls.add(c);
}
if (uniquenessAttribute.isPresent() || uniquenessFilter.isPresent())
{
final UniquenessRequestControlProperties uniquenessProperties;
if (uniquenessAttribute.isPresent())
{
uniquenessProperties = new UniquenessRequestControlProperties(
uniquenessAttribute.getValues());
if (uniquenessFilter.isPresent())
{
uniquenessProperties.setFilter(uniquenessFilter.getValue());
}
}
else
{
uniquenessProperties = new UniquenessRequestControlProperties(
uniquenessFilter.getValue());
}
if (uniquenessBaseDN.isPresent())
{
uniquenessProperties.setBaseDN(uniquenessBaseDN.getStringValue());
}
if (uniquenessMultipleAttributeBehavior.isPresent())
{
final String value =
uniquenessMultipleAttributeBehavior.getValue().toLowerCase();
switch (value)
{
case "unique-within-each-attribute":
uniquenessProperties.setMultipleAttributeBehavior(
UniquenessMultipleAttributeBehavior.
UNIQUE_WITHIN_EACH_ATTRIBUTE);
break;
case "unique-across-all-attributes-including-in-same-entry":
uniquenessProperties.setMultipleAttributeBehavior(
UniquenessMultipleAttributeBehavior.
UNIQUE_ACROSS_ALL_ATTRIBUTES_INCLUDING_IN_SAME_ENTRY);
break;
case "unique-across-all-attributes-except-in-same-entry":
uniquenessProperties.setMultipleAttributeBehavior(
UniquenessMultipleAttributeBehavior.
UNIQUE_ACROSS_ALL_ATTRIBUTES_EXCEPT_IN_SAME_ENTRY);
break;
case "unique-in-combination":
uniquenessProperties.setMultipleAttributeBehavior(
UniquenessMultipleAttributeBehavior.UNIQUE_IN_COMBINATION);
break;
}
}
if (uniquenessPreCommitValidationLevel.isPresent())
{
final String value =
uniquenessPreCommitValidationLevel.getValue().toLowerCase();
switch (value)
{
case "none":
uniquenessProperties.setPreCommitValidationLevel(
UniquenessValidationLevel.NONE);
break;
case "all-subtree-views":
uniquenessProperties.setPreCommitValidationLevel(
UniquenessValidationLevel.ALL_SUBTREE_VIEWS);
break;
case "all-backend-sets":
uniquenessProperties.setPreCommitValidationLevel(
UniquenessValidationLevel.ALL_BACKEND_SETS);
break;
case "all-available-backend-servers":
uniquenessProperties.setPreCommitValidationLevel(
UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS);
break;
}
}
if (uniquenessPostCommitValidationLevel.isPresent())
{
final String value =
uniquenessPostCommitValidationLevel.getValue().toLowerCase();
switch (value)
{
case "none":
uniquenessProperties.setPostCommitValidationLevel(
UniquenessValidationLevel.NONE);
break;
case "all-subtree-views":
uniquenessProperties.setPostCommitValidationLevel(
UniquenessValidationLevel.ALL_SUBTREE_VIEWS);
break;
case "all-backend-sets":
uniquenessProperties.setPostCommitValidationLevel(
UniquenessValidationLevel.ALL_BACKEND_SETS);
break;
case "all-available-backend-servers":
uniquenessProperties.setPostCommitValidationLevel(
UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS);
break;
}
}
final UniquenessRequestControl c =
new UniquenessRequestControl(true, null, uniquenessProperties);
addControls.add(c);
modifyControls.add(c);
modifyDNControls.add(c);
}
}
/**
* Creates the password update behavior request control that should be
* included in add and modify requests.
*
* @param argIdentifier The identifier string for the argument used to
* configure the password update behavior request
* control.
* @param argValues The set of values for the password update behavior
* request control.
*
* @return The password update behavior request control that was created.
*
* @throws LDAPException If a problem is encountered while creating the
* control.
*/
static PasswordUpdateBehaviorRequestControl
createPasswordUpdateBehaviorRequestControl(
final String argIdentifier, final List argValues)
throws LDAPException
{
final PasswordUpdateBehaviorRequestControlProperties properties =
new PasswordUpdateBehaviorRequestControlProperties();
for (final String argValue : argValues)
{
int delimiterPos = argValue.indexOf('=');
if (delimiterPos < 0)
{
delimiterPos = argValue.indexOf(':');
}
if ((delimiterPos <= 0) || (delimiterPos >= (argValue.length() - 1)))
{
throw new LDAPException(ResultCode.PARAM_ERROR,
ERR_LDAPMODIFY_MALFORMED_PW_UPDATE_BEHAVIOR.get(argValue,
argIdentifier));
}
final String name = argValue.substring(0, delimiterPos).trim();
final String value = argValue.substring(delimiterPos+1).trim();
if (name.equalsIgnoreCase("is-self-change") ||
name.equalsIgnoreCase("self-change") ||
name.equalsIgnoreCase("isSelfChange") ||
name.equalsIgnoreCase("selfChange"))
{
properties.setIsSelfChange(parseBooleanValue(name, value));
}
else if (name.equalsIgnoreCase("allow-pre-encoded-password") ||
name.equalsIgnoreCase("allow-pre-encoded-passwords") ||
name.equalsIgnoreCase("allow-pre-encoded") ||
name.equalsIgnoreCase("allowPreEncodedPassword") ||
name.equalsIgnoreCase("allowPreEncodedPasswords") ||
name.equalsIgnoreCase("allowPreEncoded"))
{
properties.setAllowPreEncodedPassword(parseBooleanValue(name, value));
}
else if (name.equalsIgnoreCase("skip-password-validation") ||
name.equalsIgnoreCase("skip-password-validators") ||
name.equalsIgnoreCase("skip-validation") ||
name.equalsIgnoreCase("skip-validators") ||
name.equalsIgnoreCase("skipPasswordValidation") ||
name.equalsIgnoreCase("skipPasswordValidators") ||
name.equalsIgnoreCase("skipValidation") ||
name.equalsIgnoreCase("skipValidators"))
{
properties.setSkipPasswordValidation(parseBooleanValue(name, value));
}
else if (name.equalsIgnoreCase("ignore-password-history") ||
name.equalsIgnoreCase("skip-password-history") ||
name.equalsIgnoreCase("ignore-history") ||
name.equalsIgnoreCase("skip-history") ||
name.equalsIgnoreCase("ignorePasswordHistory") ||
name.equalsIgnoreCase("skipPasswordHistory") ||
name.equalsIgnoreCase("ignoreHistory") ||
name.equalsIgnoreCase("skipHistory"))
{
properties.setIgnorePasswordHistory(parseBooleanValue(name, value));
}
else if (name.equalsIgnoreCase("ignore-minimum-password-age") ||
name.equalsIgnoreCase("ignore-min-password-age") ||
name.equalsIgnoreCase("ignore-password-age") ||
name.equalsIgnoreCase("skip-minimum-password-age") ||
name.equalsIgnoreCase("skip-min-password-age") ||
name.equalsIgnoreCase("skip-password-age") ||
name.equalsIgnoreCase("ignoreMinimumPasswordAge") ||
name.equalsIgnoreCase("ignoreMinPasswordAge") ||
name.equalsIgnoreCase("ignorePasswordAge") ||
name.equalsIgnoreCase("skipMinimumPasswordAge") ||
name.equalsIgnoreCase("skipMinPasswordAge") ||
name.equalsIgnoreCase("skipPasswordAge"))
{
properties.setIgnoreMinimumPasswordAge(parseBooleanValue(name, value));
}
else if (name.equalsIgnoreCase("password-storage-scheme") ||
name.equalsIgnoreCase("password-scheme") ||
name.equalsIgnoreCase("storage-scheme") ||
name.equalsIgnoreCase("scheme") ||
name.equalsIgnoreCase("passwordStorageScheme") ||
name.equalsIgnoreCase("passwordScheme") ||
name.equalsIgnoreCase("storageScheme"))
{
properties.setPasswordStorageScheme(value);
}
else if (name.equalsIgnoreCase("must-change-password") ||
name.equalsIgnoreCase("mustChangePassword"))
{
properties.setMustChangePassword(parseBooleanValue(name, value));
}
}
return new PasswordUpdateBehaviorRequestControl(properties, true);
}
/**
* Parses the provided value as the Boolean value for a password update
* behavior property.
*
* @param name The name of the password update behavior property being
* parsed.
* @param value The value to be parsed.
*
* @return The Boolean value that was parsed.
*
* @throws LDAPException If the provided value cannot be parsed as a
* Boolean value.
*/
private static boolean parseBooleanValue(final String name,
final String value)
throws LDAPException
{
if (value.equalsIgnoreCase("true") ||
value.equalsIgnoreCase("t") ||
value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("y") ||
value.equalsIgnoreCase("1"))
{
return true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("f") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("n") ||
value.equalsIgnoreCase("0"))
{
return false;
}
else
{
throw new LDAPException(ResultCode.PARAM_ERROR,
ERR_LDAPMODIFY_INVALID_PW_UPDATE_BOOLEAN_VALUE.get(value, name));
}
}
/**
* Performs the appropriate processing for an LDIF add change record.
*
* @param changeRecord The LDIF add change record to process.
* @param controls The set of controls to include in the request.
* @param pool The connection pool to use to communicate with
* the directory server.
* @param multiUpdateRequests The list to which the request should be added
* if it is to be processed as part of a
* multi-update operation. It may be
* {@code null} if the operation should not be
* processed via the multi-update operation.
* @param rejectWriter The LDIF writer to use for recording
* information about rejected changes. It may be
* {@code null} if no reject writer is
* configured.
*
* @return The result code obtained from processing.
*
* @throws LDAPException If the operation did not complete successfully
* and processing should not continue.
*/
private ResultCode doAdd(final LDIFAddChangeRecord changeRecord,
final List controls,
final LDAPConnectionPool pool,
final List multiUpdateRequests,
final LDIFWriter rejectWriter)
throws LDAPException
{
// Create the add request to process.
final AddRequest addRequest = changeRecord.toAddRequest(true);
for (final Control c : controls)
{
addRequest.addControl(c);
}
// If we should provide support for undelete operations and the entry
// includes the ds-undelete-from-dn attribute, then add the undelete request
// control.
if (allowUndelete.isPresent() &&
addRequest.hasAttribute(ATTR_UNDELETE_FROM_DN))
{
addRequest.addControl(new UndeleteRequestControl());
}
// If the entry to add includes a password, then add a password validation
// details request control if appropriate.
if (passwordValidationDetails.isPresent())
{
final Entry entryToAdd = addRequest.toEntry();
if ((! entryToAdd.getAttributesWithOptions(ATTR_USER_PASSWORD,
null).isEmpty()) ||
(! entryToAdd.getAttributesWithOptions(ATTR_AUTH_PASSWORD,
null).isEmpty()))
{
addRequest.addControl(new PasswordValidationDetailsRequestControl());
}
}
// If the operation should be processed in a multi-update operation, then
// just add the request to the list and return without doing anything else.
if (multiUpdateErrorBehavior.isPresent())
{
multiUpdateRequests.add(addRequest);
commentToOut(INFO_LDAPMODIFY_ADD_ADDED_TO_MULTI_UPDATE.get(
addRequest.getDN()));
return ResultCode.SUCCESS;
}
// If the --dryRun argument was provided, then we'll stop here.
if (dryRun.isPresent())
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_ADD.get(addRequest.getDN(),
dryRun.getIdentifierString()));
return ResultCode.SUCCESS;
}
// Process the add operation and get the result.
commentToOut(INFO_LDAPMODIFY_ADDING_ENTRY.get(addRequest.getDN()));
if (verbose.isPresent())
{
for (final String ldifLine :
addRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN))
{
out(ldifLine);
}
out();
}
LDAPResult addResult;
try
{
addResult = pool.add(addRequest);
}
catch (final LDAPException le)
{
Debug.debugException(le);
addResult = le.toLDAPResult();
}
// Display information about the result.
displayResult(addResult, useTransaction.isPresent());
// See if the add operation succeeded or failed. If it failed, and we
// should end all processing, then throw an exception.
switch (addResult.getResultCode().intValue())
{
case ResultCode.SUCCESS_INT_VALUE:
case ResultCode.NO_OPERATION_INT_VALUE:
break;
case ResultCode.ASSERTION_FAILED_INT_VALUE:
writeRejectedChange(rejectWriter,
INFO_LDAPMODIFY_ASSERTION_FAILED.get(addRequest.getDN(),
String.valueOf(assertionFilter.getValue())),
addRequest.toLDIFChangeRecord(), addResult);
throw new LDAPException(addResult);
default:
writeRejectedChange(rejectWriter, null, addRequest.toLDIFChangeRecord(),
addResult);
if (useTransaction.isPresent() || (! continueOnError.isPresent()))
{
throw new LDAPException(addResult);
}
break;
}
return addResult.getResultCode();
}
/**
* Performs the appropriate processing for an LDIF delete change record.
*
* @param changeRecord The LDIF delete change record to process.
* @param controls The set of controls to include in the request.
* @param pool The connection pool to use to communicate with
* the directory server.
* @param multiUpdateRequests The list to which the request should be added
* if it is to be processed as part of a
* multi-update operation. It may be
* {@code null} if the operation should not be
* processed via the multi-update operation.
* @param rejectWriter The LDIF writer to use for recording
* information about rejected changes. It may be
* {@code null} if no reject writer is
* configured.
*
* @return The result code obtained from processing.
*
* @throws LDAPException If the operation did not complete successfully
* and processing should not continue.
*/
private ResultCode doDelete(final LDIFDeleteChangeRecord changeRecord,
final List controls,
final LDAPConnectionPool pool,
final List multiUpdateRequests,
final LDIFWriter rejectWriter)
throws LDAPException
{
// If we should perform a client-side subtree delete, then do that
// differently.
if (clientSideSubtreeDelete.isPresent())
{
return doClientSideSubtreeDelete(changeRecord, controls, pool,
rejectWriter);
}
// Create the delete request to process.
final DeleteRequest deleteRequest = changeRecord.toDeleteRequest(true);
for (final Control c : controls)
{
deleteRequest.addControl(c);
}
// If the operation should be processed in a multi-update operation, then
// just add the request to the list and return without doing anything else.
if (multiUpdateErrorBehavior.isPresent())
{
multiUpdateRequests.add(deleteRequest);
commentToOut(INFO_LDAPMODIFY_DELETE_ADDED_TO_MULTI_UPDATE.get(
deleteRequest.getDN()));
return ResultCode.SUCCESS;
}
// If the --dryRun argument was provided, then we'll stop here.
if (dryRun.isPresent())
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_DELETE.get(deleteRequest.getDN(),
dryRun.getIdentifierString()));
return ResultCode.SUCCESS;
}
// Process the delete operation and get the result.
commentToOut(INFO_LDAPMODIFY_DELETING_ENTRY.get(deleteRequest.getDN()));
if (verbose.isPresent())
{
for (final String ldifLine :
deleteRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN))
{
out(ldifLine);
}
out();
}
LDAPResult deleteResult;
try
{
deleteResult = pool.delete(deleteRequest);
}
catch (final LDAPException le)
{
Debug.debugException(le);
deleteResult = le.toLDAPResult();
}
// Display information about the result.
displayResult(deleteResult, useTransaction.isPresent());
// See if the delete operation succeeded or failed. If it failed, and we
// should end all processing, then throw an exception.
switch (deleteResult.getResultCode().intValue())
{
case ResultCode.SUCCESS_INT_VALUE:
case ResultCode.NO_OPERATION_INT_VALUE:
break;
case ResultCode.ASSERTION_FAILED_INT_VALUE:
writeRejectedChange(rejectWriter,
INFO_LDAPMODIFY_ASSERTION_FAILED.get(deleteRequest.getDN(),
String.valueOf(assertionFilter.getValue())),
deleteRequest.toLDIFChangeRecord(), deleteResult);
throw new LDAPException(deleteResult);
default:
writeRejectedChange(rejectWriter, null,
deleteRequest.toLDIFChangeRecord(), deleteResult);
if (useTransaction.isPresent() || (! continueOnError.isPresent()))
{
throw new LDAPException(deleteResult);
}
break;
}
return deleteResult.getResultCode();
}
/**
* Performs the appropriate processing for an LDIF delete change record.
*
* @param changeRecord The LDIF delete change record to process.
* @param controls The set of controls to include in the request.
* @param pool The connection pool to use to communicate with the
* directory server.
* @param rejectWriter The LDIF writer to use for recording information
* about rejected changes. It may be {@code null} if no
* reject writer is configured.
*
* @return The result code obtained from processing.
*
* @throws LDAPException If the operation did not complete successfully
* and processing should not continue.
*/
private ResultCode doClientSideSubtreeDelete(
final LDIFChangeRecord changeRecord,
final List controls,
final LDAPConnectionPool pool,
final LDIFWriter rejectWriter)
throws LDAPException
{
// Create the subtree deleter with the provided set of controls. Make sure
// to include any controls in the delete change record itself.
final List additionalControls;
if (changeRecord.getControls().isEmpty())
{
additionalControls = controls;
}
else
{
additionalControls = new ArrayList<>(controls.size() +
changeRecord.getControls().size());
additionalControls.addAll(changeRecord.getControls());
additionalControls.addAll(controls);
}
final SubtreeDeleter subtreeDeleter = new SubtreeDeleter();
subtreeDeleter.setAdditionalDeleteControls(additionalControls);
// Perform the subtree delete.
commentToOut(INFO_LDAPMODIFY_CLIENT_SIDE_DELETING_SUBTREE.get(
changeRecord.getDN()));
final SubtreeDeleterResult subtreeDeleterResult =
subtreeDeleter.delete(pool, changeRecord.getDN());
// Evaluate the result of the subtree delete.
final LDAPResult finalResult;
if (subtreeDeleterResult.completelySuccessful())
{
final long entriesDeleted = subtreeDeleterResult.getEntriesDeleted();
if (entriesDeleted == 0L)
{
// This means that the base entry did not exist. Even though the
// subtree deleter returned a successful result, we'll use a final
// result of "no such object".
finalResult = new LDAPResult(-1, ResultCode.NO_SUCH_OBJECT,
ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_0_ENTRIES.get(
changeRecord.getDN()),
null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
}
else if (entriesDeleted == 1L)
{
// This means the base entry existed (and we deleted it successfully),
// but did not have any subordinates.
finalResult = new LDAPResult(-1, ResultCode.SUCCESS,
INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_1_ENTRY.get(
changeRecord.getDN()),
null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
}
else
{
// This means that the base entry existed and had subordinates, and we
// deleted all of them successfully.
finalResult = new LDAPResult(-1, ResultCode.SUCCESS,
INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_ENTRIES.get(
subtreeDeleterResult.getEntriesDeleted(),
changeRecord.getDN()),
null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
}
}
else
{
// If there was a search error, then display information about it.
final SearchResult searchError = subtreeDeleterResult.getSearchError();
if (searchError != null)
{
commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SEARCH_ERROR.get());
displayResult(searchError, false);
err("#");
}
final SortedMap deleteErrors =
subtreeDeleterResult.getDeleteErrorsDescendingMap();
for (final Map.Entry deleteError : deleteErrors.entrySet())
{
commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_ERROR.get(
String.valueOf(deleteError.getKey())));
displayResult(deleteError.getValue(), false);
err("#");
}
ResultCode resultCode = ResultCode.OTHER;
final StringBuilder buffer = new StringBuilder();
buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_ERR_BASE.get());
if (searchError != null)
{
resultCode = searchError.getResultCode();
buffer.append(" ");
buffer.append(
ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_SEARCH_ERR.get());
}
if (! deleteErrors.isEmpty())
{
resultCode = deleteErrors.values().iterator().next().getResultCode();
buffer.append(" ");
final int numDeleteErrors = deleteErrors.size();
if (numDeleteErrors == 1)
{
buffer.append(
ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT_1.get());
}
else
{
buffer.append(
ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT.get(
numDeleteErrors));
}
}
buffer.append(" ");
final long deletedCount = subtreeDeleterResult.getEntriesDeleted();
if (deletedCount == 1L)
{
buffer.append(
ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT_1.get());
}
else
{
buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT.get(
deletedCount));
}
finalResult = new LDAPResult(-1, resultCode, buffer.toString(), null,
StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
}
// Display information about the final result.
displayResult(finalResult, useTransaction.isPresent());
// See if the delete operation succeeded or failed. If it failed, and we
// should end all processing, then throw an exception.
switch (finalResult.getResultCode().intValue())
{
case ResultCode.SUCCESS_INT_VALUE:
case ResultCode.NO_OPERATION_INT_VALUE:
break;
default:
writeRejectedChange(rejectWriter, null, changeRecord, finalResult);
if (! continueOnError.isPresent())
{
throw new LDAPException(finalResult);
}
break;
}
return finalResult.getResultCode();
}
/**
* Performs the appropriate processing for an LDIF modify change record.
*
* @param changeRecord The LDIF modify change record to process.
* @param controls The set of controls to include in the request.
* @param pool The connection pool to use to communicate with
* the directory server.
* @param multiUpdateRequests The list to which the request should be added
* if it is to be processed as part of a
* multi-update operation. It may be
* {@code null} if the operation should not be
* processed via the multi-update operation.
* @param rejectWriter The LDIF writer to use for recording
* information about rejected changes. It may be
* {@code null} if no reject writer is
* configured.
*
* @return The result code obtained from processing.
*
* @throws LDAPException If the operation did not complete successfully
* and processing should not continue.
*/
ResultCode doModify(final LDIFModifyChangeRecord changeRecord,
final List controls,
final LDAPConnectionPool pool,
final List multiUpdateRequests,
final LDIFWriter rejectWriter)
throws LDAPException
{
// Create the modify request to process.
final ModifyRequest modifyRequest = changeRecord.toModifyRequest(true);
for (final Control c : controls)
{
modifyRequest.addControl(c);
}
// If the modify request includes a password change, then add any controls
// that are specific to that.
if (retireCurrentPassword.isPresent() || purgeCurrentPassword.isPresent() ||
passwordValidationDetails.isPresent())
{
for (final Modification m : modifyRequest.getModifications())
{
final String baseName = m.getAttribute().getBaseName();
if (baseName.equalsIgnoreCase(ATTR_USER_PASSWORD) ||
baseName.equalsIgnoreCase(ATTR_AUTH_PASSWORD))
{
if (retireCurrentPassword.isPresent())
{
modifyRequest.addControl(new RetirePasswordRequestControl(false));
}
else if (purgeCurrentPassword.isPresent())
{
modifyRequest.addControl(new PurgePasswordRequestControl(false));
}
if (passwordValidationDetails.isPresent())
{
modifyRequest.addControl(
new PasswordValidationDetailsRequestControl());
}
break;
}
}
}
// If the operation should be processed in a multi-update operation, then
// just add the request to the list and return without doing anything else.
if (multiUpdateErrorBehavior.isPresent())
{
multiUpdateRequests.add(modifyRequest);
commentToOut(INFO_LDAPMODIFY_MODIFY_ADDED_TO_MULTI_UPDATE.get(
modifyRequest.getDN()));
return ResultCode.SUCCESS;
}
// If the --dryRun argument was provided, then we'll stop here.
if (dryRun.isPresent())
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_MODIFY.get(modifyRequest.getDN(),
dryRun.getIdentifierString()));
return ResultCode.SUCCESS;
}
// Process the modify operation and get the result.
commentToOut(INFO_LDAPMODIFY_MODIFYING_ENTRY.get(modifyRequest.getDN()));
if (verbose.isPresent())
{
for (final String ldifLine :
modifyRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN))
{
out(ldifLine);
}
out();
}
LDAPResult modifyResult;
try
{
modifyResult = pool.modify(modifyRequest);
}
catch (final LDAPException le)
{
Debug.debugException(le);
modifyResult = le.toLDAPResult();
}
// Display information about the result.
displayResult(modifyResult, useTransaction.isPresent());
// See if the modify operation succeeded or failed. If it failed, and we
// should end all processing, then throw an exception.
switch (modifyResult.getResultCode().intValue())
{
case ResultCode.SUCCESS_INT_VALUE:
case ResultCode.NO_OPERATION_INT_VALUE:
break;
case ResultCode.ASSERTION_FAILED_INT_VALUE:
writeRejectedChange(rejectWriter,
INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyRequest.getDN(),
String.valueOf(assertionFilter.getValue())),
modifyRequest.toLDIFChangeRecord(), modifyResult);
throw new LDAPException(modifyResult);
default:
writeRejectedChange(rejectWriter, null,
modifyRequest.toLDIFChangeRecord(), modifyResult);
if (useTransaction.isPresent() || (! continueOnError.isPresent()))
{
throw new LDAPException(modifyResult);
}
break;
}
return modifyResult.getResultCode();
}
/**
* Performs the appropriate processing for an LDIF modify DN change record.
*
* @param changeRecord The LDIF modify DN change record to process.
* @param controls The set of controls to include in the request.
* @param pool The connection pool to use to communicate with
* the directory server.
* @param multiUpdateRequests The list to which the request should be added
* if it is to be processed as part of a
* multi-update operation. It may be
* {@code null} if the operation should not be
* processed via the multi-update operation.
* @param rejectWriter The LDIF writer to use for recording
* information about rejected changes. It may be
* {@code null} if no reject writer is
* configured.
*
* @return The result code obtained from processing.
*
* @throws LDAPException If the operation did not complete successfully
* and processing should not continue.
*/
private ResultCode doModifyDN(final LDIFModifyDNChangeRecord changeRecord,
final List controls,
final LDAPConnectionPool pool,
final List multiUpdateRequests,
final LDIFWriter rejectWriter)
throws LDAPException
{
// Create the modify DN request to process.
final ModifyDNRequest modifyDNRequest =
changeRecord.toModifyDNRequest(true);
for (final Control c : controls)
{
modifyDNRequest.addControl(c);
}
// If the operation should be processed in a multi-update operation, then
// just add the request to the list and return without doing anything else.
if (multiUpdateErrorBehavior.isPresent())
{
multiUpdateRequests.add(modifyDNRequest);
commentToOut(INFO_LDAPMODIFY_MODIFY_DN_ADDED_TO_MULTI_UPDATE.get(
modifyDNRequest.getDN()));
return ResultCode.SUCCESS;
}
// Try to determine the new DN that the entry will have after the operation.
DN newDN = null;
try
{
newDN = changeRecord.getNewDN();
}
catch (final Exception e)
{
Debug.debugException(e);
// This should only happen if the provided DN, new RDN, or new superior DN
// was malformed. Although we could reject the operation now, we'll go
// ahead and send the request to the server in case it has some special
// handling for the DN.
}
// If the --dryRun argument was provided, then we'll stop here.
if (dryRun.isPresent())
{
if (modifyDNRequest.getNewSuperiorDN() == null)
{
if (newDN == null)
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME.get(
modifyDNRequest.getDN(), dryRun.getIdentifierString()));
}
else
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME_TO.get(
modifyDNRequest.getDN(), newDN.toString(),
dryRun.getIdentifierString()));
}
}
else
{
if (newDN == null)
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE.get(
modifyDNRequest.getDN(), dryRun.getIdentifierString()));
}
else
{
commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE_TO.get(
modifyDNRequest.getDN(), newDN.toString(),
dryRun.getIdentifierString()));
}
}
return ResultCode.SUCCESS;
}
// Process the modify DN operation and get the result.
final String currentDN = modifyDNRequest.getDN();
if (modifyDNRequest.getNewSuperiorDN() == null)
{
if (newDN == null)
{
commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY.get(currentDN));
}
else
{
commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY_TO.get(currentDN,
newDN.toString()));
}
}
else
{
if (newDN == null)
{
commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY.get(currentDN));
}
else
{
commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY_TO.get(currentDN,
newDN.toString()));
}
}
if (verbose.isPresent())
{
for (final String ldifLine :
modifyDNRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN))
{
out(ldifLine);
}
out();
}
LDAPResult modifyDNResult;
try
{
modifyDNResult = pool.modifyDN(modifyDNRequest);
}
catch (final LDAPException le)
{
Debug.debugException(le);
modifyDNResult = le.toLDAPResult();
}
// Display information about the result.
displayResult(modifyDNResult, useTransaction.isPresent());
// See if the modify DN operation succeeded or failed. If it failed, and we
// should end all processing, then throw an exception.
switch (modifyDNResult.getResultCode().intValue())
{
case ResultCode.SUCCESS_INT_VALUE:
case ResultCode.NO_OPERATION_INT_VALUE:
break;
case ResultCode.ASSERTION_FAILED_INT_VALUE:
writeRejectedChange(rejectWriter,
INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyDNRequest.getDN(),
String.valueOf(assertionFilter.getValue())),
modifyDNRequest.toLDIFChangeRecord(), modifyDNResult);
throw new LDAPException(modifyDNResult);
default:
writeRejectedChange(rejectWriter, null,
modifyDNRequest.toLDIFChangeRecord(), modifyDNResult);
if (useTransaction.isPresent() || (! continueOnError.isPresent()))
{
throw new LDAPException(modifyDNResult);
}
break;
}
return modifyDNResult.getResultCode();
}
/**
* Displays information about the provided result, including special
* processing for a number of supported response controls.
*
* @param result The result to examine.
* @param inTransaction Indicates whether the operation is part of a
* transaction.
*/
private void displayResult(final LDAPResult result,
final boolean inTransaction)
{
final ArrayList resultLines = new ArrayList<>(10);
ResultUtils.formatResult(resultLines, result, true, inTransaction, 0,
WRAP_COLUMN);
if (result.getResultCode() == ResultCode.SUCCESS)
{
for (final String line : resultLines)
{
out(line);
}
out();
}
else
{
for (final String line : resultLines)
{
err(line);
}
err();
}
}
/**
* Writes a line-wrapped, commented version of the provided message to
* standard output.
*
* @param message The message to be written.
*/
private void commentToOut(final String message)
{
for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2))
{
out("# ", line);
}
}
/**
* Writes a line-wrapped, commented version of the provided message to
* standard error.
*
* @param message The message to be written.
*/
private void commentToErr(final String message)
{
for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2))
{
err("# ", line);
}
}
/**
* Writes information about the rejected change to the reject writer.
*
* @param writer The LDIF writer to which the information should be
* written. It may be {@code null} if no reject file is
* configured.
* @param comment The comment to include before the change record, in
* addition to the comment generated from the provided
* LDAP result. It may be {@code null} if no additional
* comment should be included.
* @param changeRecord The LDIF change record to be written. It must not
* be {@code null}.
* @param ldapResult The LDAP result for the failed operation. It must
* not be {@code null}.
*/
private void writeRejectedChange(final LDIFWriter writer,
final String comment,
final LDIFChangeRecord changeRecord,
final LDAPResult ldapResult)
{
if (writer == null)
{
return;
}
final StringBuilder buffer = new StringBuilder();
if (comment != null)
{
buffer.append(comment);
buffer.append(StaticUtils.EOL);
buffer.append(StaticUtils.EOL);
}
final ArrayList resultLines = new ArrayList<>(10);
ResultUtils.formatResult(resultLines, ldapResult, false, false, 0, 0);
for (final String resultLine : resultLines)
{
buffer.append(resultLine);
buffer.append(StaticUtils.EOL);
}
writeRejectedChange(writer, buffer.toString(), changeRecord);
}
/**
* Writes information about the rejected change to the reject writer.
*
* @param writer The LDIF writer to which the information should be
* written. It may be {@code null} if no reject file is
* configured.
* @param comment The comment to include before the change record. It
* may be {@code null} if no comment should be included.
* @param changeRecord The LDIF change record to be written. It may be
* {@code null} if only a comment should be written.
*/
void writeRejectedChange(final LDIFWriter writer, final String comment,
final LDIFChangeRecord changeRecord)
{
if (writer == null)
{
return;
}
if (rejectWritten.compareAndSet(false, true))
{
try
{
writer.writeVersionHeader();
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
try
{
if (comment != null)
{
writer.writeComment(comment, true, false);
}
if (changeRecord != null)
{
writer.writeChangeRecord(changeRecord);
}
}
catch (final Exception e)
{
Debug.debugException(e);
commentToErr(ERR_LDAPMODIFY_UNABLE_TO_WRITE_REJECTED_CHANGE.get(
rejectFile.getValue().getAbsolutePath(),
StaticUtils.getExceptionMessage(e)));
}
}
/**
* {@inheritDoc}
*/
@Override()
public void handleUnsolicitedNotification(final LDAPConnection connection,
final ExtendedResult notification)
{
final ArrayList lines = new ArrayList<>(10);
ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0,
WRAP_COLUMN);
for (final String line : lines)
{
err(line);
}
err();
}
/**
* {@inheritDoc}
*/
@Override()
public LinkedHashMap getExampleUsages()
{
final LinkedHashMap examples =
new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
final String[] args1 =
{
"--hostname", "ldap.example.com",
"--port", "389",
"--bindDN", "uid=admin,dc=example,dc=com",
"--bindPassword", "password",
"--defaultAdd"
};
examples.put(args1, INFO_LDAPMODIFY_EXAMPLE_1.get());
final String[] args2 =
{
"--hostname", "ds1.example.com",
"--port", "636",
"--hostname", "ds2.example.com",
"--port", "636",
"--useSSL",
"--bindDN", "uid=admin,dc=example,dc=com",
"--bindPassword", "password",
"--filename", "changes.ldif",
"--modifyEntriesMatchingFilter", "(objectClass=person)",
"--searchPageSize", "100"
};
examples.put(args2, INFO_LDAPMODIFY_EXAMPLE_2.get());
return examples;
}
}