001/* 002 * Copyright 2016-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2016-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.ByteArrayInputStream; 041import java.io.File; 042import java.io.InputStream; 043import java.io.IOException; 044import java.io.OutputStream; 045import java.util.ArrayList; 046import java.util.EnumSet; 047import java.util.HashSet; 048import java.util.LinkedHashMap; 049import java.util.List; 050import java.util.Map; 051import java.util.Set; 052import java.util.SortedMap; 053import java.util.StringTokenizer; 054import java.util.concurrent.TimeUnit; 055import java.util.concurrent.atomic.AtomicBoolean; 056 057import com.unboundid.asn1.ASN1OctetString; 058import com.unboundid.ldap.sdk.AddRequest; 059import com.unboundid.ldap.sdk.Control; 060import com.unboundid.ldap.sdk.DeleteRequest; 061import com.unboundid.ldap.sdk.DN; 062import com.unboundid.ldap.sdk.Entry; 063import com.unboundid.ldap.sdk.ExtendedResult; 064import com.unboundid.ldap.sdk.Filter; 065import com.unboundid.ldap.sdk.LDAPConnectionOptions; 066import com.unboundid.ldap.sdk.LDAPConnection; 067import com.unboundid.ldap.sdk.LDAPConnectionPool; 068import com.unboundid.ldap.sdk.LDAPException; 069import com.unboundid.ldap.sdk.LDAPRequest; 070import com.unboundid.ldap.sdk.LDAPResult; 071import com.unboundid.ldap.sdk.LDAPSearchException; 072import com.unboundid.ldap.sdk.Modification; 073import com.unboundid.ldap.sdk.ModifyRequest; 074import com.unboundid.ldap.sdk.ModifyDNRequest; 075import com.unboundid.ldap.sdk.ResultCode; 076import com.unboundid.ldap.sdk.SearchRequest; 077import com.unboundid.ldap.sdk.SearchResult; 078import com.unboundid.ldap.sdk.SearchScope; 079import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler; 080import com.unboundid.ldap.sdk.Version; 081import com.unboundid.ldap.sdk.controls.AssertionRequestControl; 082import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 083import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl; 084import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl; 085import com.unboundid.ldap.sdk.controls.PostReadRequestControl; 086import com.unboundid.ldap.sdk.controls.PreReadRequestControl; 087import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl; 088import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; 089import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl; 090import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl; 091import com.unboundid.ldap.sdk.controls.TransactionSpecificationRequestControl; 092import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedRequest; 093import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedResult; 094import com.unboundid.ldap.sdk.extensions.EndTransactionExtendedRequest; 095import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel; 096import com.unboundid.ldap.sdk.unboundidds.controls. 097 AssuredReplicationRequestControl; 098import com.unboundid.ldap.sdk.unboundidds.controls. 099 AssuredReplicationRemoteLevel; 100import com.unboundid.ldap.sdk.unboundidds.controls. 101 GeneratePasswordRequestControl; 102import com.unboundid.ldap.sdk.unboundidds.controls. 103 GetAuthorizationEntryRequestControl; 104import com.unboundid.ldap.sdk.unboundidds.controls. 105 GetBackendSetIDRequestControl; 106import com.unboundid.ldap.sdk.unboundidds.controls. 107 GetUserResourceLimitsRequestControl; 108import com.unboundid.ldap.sdk.unboundidds.controls.GetServerIDRequestControl; 109import com.unboundid.ldap.sdk.unboundidds.controls.HardDeleteRequestControl; 110import com.unboundid.ldap.sdk.unboundidds.controls. 111 IgnoreNoUserModificationRequestControl; 112import com.unboundid.ldap.sdk.unboundidds.controls. 113 NameWithEntryUUIDRequestControl; 114import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl; 115import com.unboundid.ldap.sdk.unboundidds.controls. 116 OperationPurposeRequestControl; 117import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl; 118import com.unboundid.ldap.sdk.unboundidds.controls. 119 PasswordUpdateBehaviorRequestControl; 120import com.unboundid.ldap.sdk.unboundidds.controls. 121 PasswordUpdateBehaviorRequestControlProperties; 122import com.unboundid.ldap.sdk.unboundidds.controls. 123 PasswordValidationDetailsRequestControl; 124import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl; 125import com.unboundid.ldap.sdk.unboundidds.controls. 126 ReplicationRepairRequestControl; 127import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl; 128import com.unboundid.ldap.sdk.unboundidds.controls. 129 RouteToBackendSetRequestControl; 130import com.unboundid.ldap.sdk.unboundidds.controls.RouteToServerRequestControl; 131import com.unboundid.ldap.sdk.unboundidds.controls.SoftDeleteRequestControl; 132import com.unboundid.ldap.sdk.unboundidds.controls. 133 SuppressOperationalAttributeUpdateRequestControl; 134import com.unboundid.ldap.sdk.unboundidds.controls. 135 SuppressReferentialIntegrityUpdatesRequestControl; 136import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessMultipleAttributeBehavior; 137import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessRequestControl; 138import com.unboundid.ldap.sdk.unboundidds.controls. 139 UniquenessRequestControlProperties; 140import com.unboundid.ldap.sdk.unboundidds.controls.SuppressType; 141import com.unboundid.ldap.sdk.unboundidds.controls.UndeleteRequestControl; 142import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessValidationLevel; 143import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateErrorBehavior; 144import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateExtendedRequest; 145import com.unboundid.ldap.sdk.unboundidds.extensions. 146 StartAdministrativeSessionExtendedRequest; 147import com.unboundid.ldap.sdk.unboundidds.extensions. 148 StartAdministrativeSessionPostConnectProcessor; 149import com.unboundid.ldif.LDIFAddChangeRecord; 150import com.unboundid.ldif.LDIFChangeRecord; 151import com.unboundid.ldif.LDIFDeleteChangeRecord; 152import com.unboundid.ldif.LDIFException; 153import com.unboundid.ldif.LDIFModifyChangeRecord; 154import com.unboundid.ldif.LDIFModifyDNChangeRecord; 155import com.unboundid.ldif.LDIFReader; 156import com.unboundid.ldif.LDIFWriter; 157import com.unboundid.ldif.TrailingSpaceBehavior; 158import com.unboundid.util.Debug; 159import com.unboundid.util.DNFileReader; 160import com.unboundid.util.FilterFileReader; 161import com.unboundid.util.FixedRateBarrier; 162import com.unboundid.util.LDAPCommandLineTool; 163import com.unboundid.util.StaticUtils; 164import com.unboundid.util.SubtreeDeleter; 165import com.unboundid.util.SubtreeDeleterResult; 166import com.unboundid.util.ThreadSafety; 167import com.unboundid.util.ThreadSafetyLevel; 168import com.unboundid.util.args.ArgumentException; 169import com.unboundid.util.args.ArgumentParser; 170import com.unboundid.util.args.BooleanArgument; 171import com.unboundid.util.args.ControlArgument; 172import com.unboundid.util.args.DNArgument; 173import com.unboundid.util.args.DurationArgument; 174import com.unboundid.util.args.FileArgument; 175import com.unboundid.util.args.FilterArgument; 176import com.unboundid.util.args.IntegerArgument; 177import com.unboundid.util.args.StringArgument; 178 179import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 180 181 182 183/** 184 * This class provides an implementation of an LDAP command-line tool that may 185 * be used to apply changes to a directory server. The changes to apply (which 186 * may include add, delete, modify, and modify DN operations) will be read in 187 * LDIF form, either from standard input or a specified file or set of files. 188 * This is a much more full-featured tool than the 189 * {@link com.unboundid.ldap.sdk.examples.LDAPModify} tool 190 * <BR> 191 * <BLOCKQUOTE> 192 * <B>NOTE:</B> This class, and other classes within the 193 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 194 * supported for use against Ping Identity, UnboundID, and 195 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 196 * for proprietary functionality or for external specifications that are not 197 * considered stable or mature enough to be guaranteed to work in an 198 * interoperable way with other types of LDAP servers. 199 * </BLOCKQUOTE> 200 */ 201@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 202public final class LDAPModify 203 extends LDAPCommandLineTool 204 implements UnsolicitedNotificationHandler 205{ 206 /** 207 * The column at which output should be wrapped. 208 */ 209 private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 210 211 212 213 /** 214 * The name of the attribute type used to specify a password in the 215 * authentication password syntax as described in RFC 3112. 216 */ 217 private static final String ATTR_AUTH_PASSWORD = "authPassword"; 218 219 220 221 /** 222 * The name of the attribute type used to specify the DN of the soft-deleted 223 * entry to be restored via an undelete operation. 224 */ 225 private static final String ATTR_UNDELETE_FROM_DN = "ds-undelete-from-dn"; 226 227 228 229 /** 230 * The name of the attribute type used to specify a password in the 231 * userPassword syntax. 232 */ 233 private static final String ATTR_USER_PASSWORD = "userPassword"; 234 235 236 237 /** 238 * The long identifier for the argument used to specify the desired assured 239 * replication local level. 240 */ 241 private static final String ARG_ASSURED_REPLICATION_LOCAL_LEVEL = 242 "assuredReplicationLocalLevel"; 243 244 245 246 /** 247 * The long identifier for the argument used to specify the desired assured 248 * replication remote level. 249 */ 250 private static final String ARG_ASSURED_REPLICATION_REMOTE_LEVEL = 251 "assuredReplicationRemoteLevel"; 252 253 254 255 /** 256 * The long identifier for the argument used to specify the desired assured 257 * timeout. 258 */ 259 private static final String ARG_ASSURED_REPLICATION_TIMEOUT = 260 "assuredReplicationTimeout"; 261 262 263 264 /** 265 * The long identifier for the argument used to specify the path to an LDIF 266 * file containing changes to apply. 267 */ 268 private static final String ARG_LDIF_FILE = "ldifFile"; 269 270 271 272 /** 273 * The long identifier for the argument used to specify the simple paged 274 * results page size to use when modifying entries that match a provided 275 * filter. 276 */ 277 private static final String ARG_SEARCH_PAGE_SIZE = "searchPageSize"; 278 279 280 281 // The set of arguments supported by this program. 282 private BooleanArgument allowUndelete = null; 283 private BooleanArgument assuredReplication = null; 284 private BooleanArgument authorizationIdentity = null; 285 private BooleanArgument clientSideSubtreeDelete = null; 286 private BooleanArgument continueOnError = null; 287 private BooleanArgument defaultAdd = null; 288 private BooleanArgument dryRun = null; 289 private BooleanArgument followReferrals = null; 290 private BooleanArgument generatePassword = null; 291 private BooleanArgument getBackendSetID = null; 292 private BooleanArgument getServerID = null; 293 private BooleanArgument getUserResourceLimits = null; 294 private BooleanArgument hardDelete = null; 295 private BooleanArgument ignoreNoUserModification = null; 296 private BooleanArgument manageDsaIT = null; 297 private BooleanArgument nameWithEntryUUID = null; 298 private BooleanArgument noOperation = null; 299 private BooleanArgument passwordValidationDetails = null; 300 private BooleanArgument permissiveModify = null; 301 private BooleanArgument purgeCurrentPassword = null; 302 private BooleanArgument replicationRepair = null; 303 private BooleanArgument retireCurrentPassword = null; 304 private BooleanArgument retryFailedOperations = null; 305 private BooleanArgument softDelete = null; 306 private BooleanArgument stripTrailingSpaces = null; 307 private BooleanArgument serverSideSubtreeDelete = null; 308 private BooleanArgument suppressReferentialIntegrityUpdates = null; 309 private BooleanArgument useAdministrativeSession = null; 310 private BooleanArgument usePasswordPolicyControl = null; 311 private BooleanArgument useTransaction = null; 312 private BooleanArgument verbose = null; 313 private ControlArgument addControl = null; 314 private ControlArgument bindControl = null; 315 private ControlArgument deleteControl = null; 316 private ControlArgument modifyControl = null; 317 private ControlArgument modifyDNControl = null; 318 private ControlArgument operationControl = null; 319 private DNArgument modifyEntryWithDN = null; 320 private DNArgument proxyV1As = null; 321 private DNArgument uniquenessBaseDN = null; 322 private DurationArgument assuredReplicationTimeout = null; 323 private FileArgument encryptionPassphraseFile = null; 324 private FileArgument ldifFile = null; 325 private FileArgument modifyEntriesMatchingFiltersFromFile = null; 326 private FileArgument modifyEntriesWithDNsFromFile = null; 327 private FileArgument rejectFile = null; 328 private FilterArgument assertionFilter = null; 329 private FilterArgument modifyEntriesMatchingFilter = null; 330 private FilterArgument uniquenessFilter = null; 331 private IntegerArgument ratePerSecond = null; 332 private IntegerArgument searchPageSize = null; 333 private StringArgument assuredReplicationLocalLevel = null; 334 private StringArgument assuredReplicationRemoteLevel = null; 335 private StringArgument characterSet = null; 336 private StringArgument getAuthorizationEntryAttribute = null; 337 private StringArgument multiUpdateErrorBehavior = null; 338 private StringArgument operationPurpose = null; 339 private StringArgument passwordUpdateBehavior = null; 340 private StringArgument postReadAttribute = null; 341 private StringArgument preReadAttribute = null; 342 private StringArgument proxyAs = null; 343 private StringArgument routeToBackendSet = null; 344 private StringArgument routeToServer = null; 345 private StringArgument suppressOperationalAttributeUpdates = null; 346 private StringArgument uniquenessAttribute = null; 347 private StringArgument uniquenessMultipleAttributeBehavior = null; 348 private StringArgument uniquenessPostCommitValidationLevel = null; 349 private StringArgument uniquenessPreCommitValidationLevel = null; 350 351 // Indicates whether we've written anything to the reject writer yet. 352 private final AtomicBoolean rejectWritten; 353 354 // The input stream from to use for standard input. 355 private final InputStream in; 356 357 // The route to backend set request controls to include in write requests. 358 private final List<RouteToBackendSetRequestControl> 359 routeToBackendSetRequestControls = new ArrayList<>(10); 360 361 362 363 /** 364 * Runs this tool with the provided command-line arguments. It will use the 365 * JVM-default streams for standard input, output, and error. 366 * 367 * @param args The command-line arguments to provide to this program. 368 */ 369 public static void main(final String... args) 370 { 371 final ResultCode resultCode = main(System.in, System.out, System.err, args); 372 if (resultCode != ResultCode.SUCCESS) 373 { 374 System.exit(Math.min(resultCode.intValue(), 255)); 375 } 376 } 377 378 379 380 /** 381 * Runs this tool with the provided streams and command-line arguments. 382 * 383 * @param in The input stream to use for standard input. If this is 384 * {@code null}, then no standard input will be used. 385 * @param out The output stream to use for standard output. If this is 386 * {@code null}, then standard output will be suppressed. 387 * @param err The output stream to use for standard error. If this is 388 * {@code null}, then standard error will be suppressed. 389 * @param args The command-line arguments provided to this program. 390 * 391 * @return The result code obtained when running the tool. Any result code 392 * other than {@link ResultCode#SUCCESS} indicates an error. 393 */ 394 public static ResultCode main(final InputStream in, final OutputStream out, 395 final OutputStream err, final String... args) 396 { 397 final LDAPModify tool = new LDAPModify(in, out, err); 398 return tool.runTool(args); 399 } 400 401 402 403 /** 404 * Creates a new instance of this tool with the provided streams. Standard 405 * input will not be available. 406 * 407 * @param out The output stream to use for standard output. If this is 408 * {@code null}, then standard output will be suppressed. 409 * @param err The output stream to use for standard error. If this is 410 * {@code null}, then standard error will be suppressed. 411 */ 412 public LDAPModify(final OutputStream out, final OutputStream err) 413 { 414 this(null, out, err); 415 } 416 417 418 419 /** 420 * Creates a new instance of this tool with the provided streams. 421 * 422 * @param in The input stream to use for standard input. If this is 423 * {@code null}, then no standard input will be used. 424 * @param out The output stream to use for standard output. If this is 425 * {@code null}, then standard output will be suppressed. 426 * @param err The output stream to use for standard error. If this is 427 * {@code null}, then standard error will be suppressed. 428 */ 429 public LDAPModify(final InputStream in, final OutputStream out, 430 final OutputStream err) 431 { 432 super(out, err); 433 434 if (in == null) 435 { 436 this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES); 437 } 438 else 439 { 440 this.in = in; 441 } 442 443 444 rejectWritten = new AtomicBoolean(false); 445 } 446 447 448 449 /** 450 * {@inheritDoc} 451 */ 452 @Override() 453 public String getToolName() 454 { 455 return "ldapmodify"; 456 } 457 458 459 460 /** 461 * {@inheritDoc} 462 */ 463 @Override() 464 public String getToolDescription() 465 { 466 return INFO_LDAPMODIFY_TOOL_DESCRIPTION.get(ARG_LDIF_FILE); 467 } 468 469 470 471 /** 472 * {@inheritDoc} 473 */ 474 @Override() 475 public String getToolVersion() 476 { 477 return Version.NUMERIC_VERSION_STRING; 478 } 479 480 481 482 /** 483 * {@inheritDoc} 484 */ 485 @Override() 486 public boolean supportsInteractiveMode() 487 { 488 return true; 489 } 490 491 492 493 /** 494 * {@inheritDoc} 495 */ 496 @Override() 497 public boolean defaultsToInteractiveMode() 498 { 499 return true; 500 } 501 502 503 504 /** 505 * {@inheritDoc} 506 */ 507 @Override() 508 public boolean supportsPropertiesFile() 509 { 510 return true; 511 } 512 513 514 515 /** 516 * {@inheritDoc} 517 */ 518 @Override() 519 public boolean supportsOutputFile() 520 { 521 return true; 522 } 523 524 525 526 /** 527 * {@inheritDoc} 528 */ 529 @Override() 530 protected boolean defaultToPromptForBindPassword() 531 { 532 return true; 533 } 534 535 536 537 /** 538 * {@inheritDoc} 539 */ 540 @Override() 541 protected boolean includeAlternateLongIdentifiers() 542 { 543 return true; 544 } 545 546 547 548 /** 549 * {@inheritDoc} 550 */ 551 @Override() 552 protected boolean supportsSSLDebugging() 553 { 554 return true; 555 } 556 557 558 559 /** 560 * {@inheritDoc} 561 */ 562 @Override() 563 protected boolean logToolInvocationByDefault() 564 { 565 return true; 566 } 567 568 569 570 /** 571 * {@inheritDoc} 572 */ 573 @Override() 574 public void addNonLDAPArguments(final ArgumentParser parser) 575 throws ArgumentException 576 { 577 ldifFile = new FileArgument('f', ARG_LDIF_FILE, false, -1, null, 578 INFO_LDAPMODIFY_ARG_DESCRIPTION_LDIF_FILE.get(), true, true, true, 579 false); 580 ldifFile.addLongIdentifier("filename", true); 581 ldifFile.addLongIdentifier("ldif-file", true); 582 ldifFile.addLongIdentifier("file-name", true); 583 ldifFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 584 parser.addArgument(ldifFile); 585 586 587 encryptionPassphraseFile = new FileArgument(null, 588 "encryptionPassphraseFile", false, 1, null, 589 INFO_LDAPMODIFY_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true, 590 true, false); 591 encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file", 592 true); 593 encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true); 594 encryptionPassphraseFile.addLongIdentifier("encryption-password-file", 595 true); 596 encryptionPassphraseFile.setArgumentGroupName( 597 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 598 parser.addArgument(encryptionPassphraseFile); 599 600 601 characterSet = new StringArgument('i', "characterSet", false, 1, 602 INFO_LDAPMODIFY_PLACEHOLDER_CHARSET.get(), 603 INFO_LDAPMODIFY_ARG_DESCRIPTION_CHARACTER_SET.get(), "UTF-8"); 604 characterSet.addLongIdentifier("encoding", true); 605 characterSet.addLongIdentifier("character-set", true); 606 characterSet.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 607 parser.addArgument(characterSet); 608 609 610 rejectFile = new FileArgument('R', "rejectFile", false, 1, null, 611 INFO_LDAPMODIFY_ARG_DESCRIPTION_REJECT_FILE.get(), false, true, true, 612 false); 613 rejectFile.addLongIdentifier("reject-file", true); 614 rejectFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 615 parser.addArgument(rejectFile); 616 617 618 verbose = new BooleanArgument('v', "verbose", 1, 619 INFO_LDAPMODIFY_ARG_DESCRIPTION_VERBOSE.get()); 620 verbose.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 621 parser.addArgument(verbose); 622 623 624 modifyEntriesMatchingFilter = new FilterArgument(null, 625 "modifyEntriesMatchingFilter", false, 0, null, 626 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRIES_MATCHING_FILTER.get( 627 ARG_SEARCH_PAGE_SIZE)); 628 modifyEntriesMatchingFilter.addLongIdentifier( 629 "modify-entries-matching-filter", true); 630 modifyEntriesMatchingFilter.setArgumentGroupName( 631 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 632 parser.addArgument(modifyEntriesMatchingFilter); 633 634 635 modifyEntriesMatchingFiltersFromFile = new FileArgument(null, 636 "modifyEntriesMatchingFiltersFromFile", false, 0, null, 637 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_FILTER_FILE.get( 638 ARG_SEARCH_PAGE_SIZE), true, false, true, false); 639 modifyEntriesMatchingFiltersFromFile.addLongIdentifier( 640 "modify-entries-matching-filters-from-file", true); 641 modifyEntriesMatchingFiltersFromFile.setArgumentGroupName( 642 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 643 parser.addArgument(modifyEntriesMatchingFiltersFromFile); 644 645 646 modifyEntryWithDN = new DNArgument(null, "modifyEntryWithDN", false, 0, 647 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRY_DN.get()); 648 modifyEntryWithDN.addLongIdentifier("modify-entry-with-dn", true); 649 modifyEntryWithDN.setArgumentGroupName( 650 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 651 parser.addArgument(modifyEntryWithDN); 652 653 654 modifyEntriesWithDNsFromFile = new FileArgument(null, 655 "modifyEntriesWithDNsFromFile", false, 0, 656 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_FILE.get(), true, 657 false, true, false); 658 modifyEntriesWithDNsFromFile.addLongIdentifier( 659 "modify-entries-with-dns-from-file", true); 660 modifyEntriesWithDNsFromFile.setArgumentGroupName( 661 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 662 parser.addArgument(modifyEntriesWithDNsFromFile); 663 664 665 searchPageSize = new IntegerArgument(null, ARG_SEARCH_PAGE_SIZE, false, 1, 666 null, 667 INFO_LDAPMODIFY_ARG_DESCRIPTION_SEARCH_PAGE_SIZE.get( 668 modifyEntriesMatchingFilter.getIdentifierString(), 669 modifyEntriesMatchingFiltersFromFile.getIdentifierString()), 670 1, Integer.MAX_VALUE); 671 searchPageSize.addLongIdentifier("search-page-size", true); 672 searchPageSize.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 673 parser.addArgument(searchPageSize); 674 675 676 retryFailedOperations = new BooleanArgument(null, "retryFailedOperations", 677 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get()); 678 retryFailedOperations.addLongIdentifier("retry-failed-operations", true); 679 retryFailedOperations.setArgumentGroupName( 680 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 681 parser.addArgument(retryFailedOperations); 682 683 684 dryRun = new BooleanArgument('n', "dryRun", 1, 685 INFO_LDAPMODIFY_ARG_DESCRIPTION_DRY_RUN.get()); 686 dryRun.addLongIdentifier("dry-run", true); 687 dryRun.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 688 parser.addArgument(dryRun); 689 690 691 defaultAdd = new BooleanArgument('a', "defaultAdd", 1, 692 INFO_LDAPMODIFY_ARG_DESCRIPTION_DEFAULT_ADD.get()); 693 defaultAdd.addLongIdentifier("default-add", true); 694 defaultAdd.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 695 parser.addArgument(defaultAdd); 696 697 698 continueOnError = new BooleanArgument('c', "continueOnError", 1, 699 INFO_LDAPMODIFY_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); 700 continueOnError.addLongIdentifier("continue-on-error", true); 701 continueOnError.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 702 parser.addArgument(continueOnError); 703 704 705 stripTrailingSpaces = new BooleanArgument(null, "stripTrailingSpaces", 1, 706 INFO_LDAPMODIFY_ARG_DESCRIPTION_STRIP_TRAILING_SPACES.get()); 707 stripTrailingSpaces.addLongIdentifier("strip-trailing-spaces", true); 708 stripTrailingSpaces.setArgumentGroupName( 709 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 710 parser.addArgument(stripTrailingSpaces); 711 712 713 714 followReferrals = new BooleanArgument(null, "followReferrals", 1, 715 INFO_LDAPMODIFY_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); 716 followReferrals.addLongIdentifier("follow-referrals", true); 717 followReferrals.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 718 parser.addArgument(followReferrals); 719 720 721 proxyAs = new StringArgument('Y', "proxyAs", false, 1, 722 INFO_PLACEHOLDER_AUTHZID.get(), 723 INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_AS.get()); 724 proxyAs.addLongIdentifier("proxyV2As", true); 725 proxyAs.addLongIdentifier("proxy-as", true); 726 proxyAs.addLongIdentifier("proxy-v2-as", true); 727 proxyAs.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 728 parser.addArgument(proxyAs); 729 730 proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, 731 INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_V1_AS.get()); 732 proxyV1As.addLongIdentifier("proxy-v1-as", true); 733 proxyV1As.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 734 parser.addArgument(proxyV1As); 735 736 737 useAdministrativeSession = new BooleanArgument(null, 738 "useAdministrativeSession", 1, 739 INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); 740 useAdministrativeSession.addLongIdentifier("use-administrative-session", 741 true); 742 useAdministrativeSession.setArgumentGroupName( 743 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 744 parser.addArgument(useAdministrativeSession); 745 746 747 operationPurpose = new StringArgument(null, "operationPurpose", false, 1, 748 INFO_PLACEHOLDER_PURPOSE.get(), 749 INFO_LDAPMODIFY_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); 750 operationPurpose.addLongIdentifier("operation-purpose", true); 751 operationPurpose.setArgumentGroupName( 752 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 753 parser.addArgument(operationPurpose); 754 755 756 manageDsaIT = new BooleanArgument(null, "useManageDsaIT", 1, 757 INFO_LDAPMODIFY_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); 758 manageDsaIT.addLongIdentifier("manageDsaIT", true); 759 manageDsaIT.addLongIdentifier("use-manage-dsa-it", true); 760 manageDsaIT.addLongIdentifier("manage-dsa-it", true); 761 manageDsaIT.setArgumentGroupName( 762 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 763 parser.addArgument(manageDsaIT); 764 765 766 useTransaction = new BooleanArgument(null, "useTransaction", 1, 767 INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_TRANSACTION.get()); 768 useTransaction.addLongIdentifier("use-transaction", true); 769 useTransaction.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 770 parser.addArgument(useTransaction); 771 772 773 final Set<String> multiUpdateErrorBehaviorAllowedValues = 774 StaticUtils.setOf("atomic", "abort-on-error", "continue-on-error"); 775 multiUpdateErrorBehavior = new StringArgument(null, 776 "multiUpdateErrorBehavior", false, 1, 777 "{atomic|abort-on-error|continue-on-error}", 778 INFO_LDAPMODIFY_ARG_DESCRIPTION_MULTI_UPDATE_ERROR_BEHAVIOR.get(), 779 multiUpdateErrorBehaviorAllowedValues); 780 multiUpdateErrorBehavior.addLongIdentifier("multi-update-error-behavior", 781 true); 782 multiUpdateErrorBehavior.setArgumentGroupName( 783 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 784 parser.addArgument(multiUpdateErrorBehavior); 785 786 787 assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, 788 INFO_PLACEHOLDER_FILTER.get(), 789 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSERTION_FILTER.get()); 790 assertionFilter.addLongIdentifier("assertion-filter", true); 791 assertionFilter.setArgumentGroupName( 792 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 793 parser.addArgument(assertionFilter); 794 795 796 authorizationIdentity = new BooleanArgument('E', 797 "authorizationIdentity", 1, 798 INFO_LDAPMODIFY_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); 799 authorizationIdentity.addLongIdentifier("reportAuthzID", true); 800 authorizationIdentity.addLongIdentifier("authorization-identity", true); 801 authorizationIdentity.addLongIdentifier("report-authzID", true); 802 authorizationIdentity.addLongIdentifier("report-authz-id", true); 803 authorizationIdentity.setArgumentGroupName( 804 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 805 parser.addArgument(authorizationIdentity); 806 807 808 generatePassword = new BooleanArgument(null, "generatePassword", 1, 809 INFO_LDAPMODIFY_ARG_DESCRIPTION_GENERATE_PASSWORD.get()); 810 generatePassword.addLongIdentifier("generatePW", true); 811 generatePassword.addLongIdentifier("generate-password", true); 812 generatePassword.addLongIdentifier("generate-pw", true); 813 generatePassword.setArgumentGroupName( 814 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 815 parser.addArgument(generatePassword); 816 817 818 getAuthorizationEntryAttribute = new StringArgument(null, 819 "getAuthorizationEntryAttribute", false, 0, 820 INFO_PLACEHOLDER_ATTR.get(), 821 INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); 822 getAuthorizationEntryAttribute.addLongIdentifier( 823 "get-authorization-entry-attribute", true); 824 getAuthorizationEntryAttribute.setArgumentGroupName( 825 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 826 parser.addArgument(getAuthorizationEntryAttribute); 827 828 829 getBackendSetID = new BooleanArgument(null, "getBackendSetID", 830 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_BACKEND_SET_ID.get()); 831 getBackendSetID.addLongIdentifier("get-backend-set-id", true); 832 getBackendSetID.setArgumentGroupName( 833 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 834 parser.addArgument(getBackendSetID); 835 836 837 getServerID = new BooleanArgument(null, "getServerID", 838 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_SERVER_ID.get()); 839 getServerID.addLongIdentifier("get-server-id", true); 840 getServerID.setArgumentGroupName( 841 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 842 parser.addArgument(getServerID); 843 844 845 getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 846 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get()); 847 getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); 848 getUserResourceLimits.setArgumentGroupName( 849 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 850 parser.addArgument(getUserResourceLimits); 851 852 853 ignoreNoUserModification = new BooleanArgument(null, 854 "ignoreNoUserModification", 1, 855 INFO_LDAPMODIFY_ARG_DESCRIPTION_IGNORE_NO_USER_MOD.get()); 856 ignoreNoUserModification.addLongIdentifier("ignore-no-user-modification", 857 true); 858 ignoreNoUserModification.setArgumentGroupName( 859 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 860 parser.addArgument(ignoreNoUserModification); 861 862 863 preReadAttribute = new StringArgument(null, "preReadAttribute", false, -1, 864 INFO_PLACEHOLDER_ATTR.get(), 865 INFO_LDAPMODIFY_ARG_DESCRIPTION_PRE_READ_ATTRIBUTE.get()); 866 preReadAttribute.addLongIdentifier("preReadAttributes", true); 867 preReadAttribute.addLongIdentifier("pre-read-attribute", true); 868 preReadAttribute.addLongIdentifier("pre-read-attributes", true); 869 preReadAttribute.setArgumentGroupName( 870 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 871 parser.addArgument(preReadAttribute); 872 873 874 postReadAttribute = new StringArgument(null, "postReadAttribute", false, 875 -1, INFO_PLACEHOLDER_ATTR.get(), 876 INFO_LDAPMODIFY_ARG_DESCRIPTION_POST_READ_ATTRIBUTE.get()); 877 postReadAttribute.addLongIdentifier("postReadAttributes", true); 878 postReadAttribute.addLongIdentifier("post-read-attribute", true); 879 postReadAttribute.addLongIdentifier("post-read-attributes", true); 880 postReadAttribute.setArgumentGroupName( 881 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 882 parser.addArgument(postReadAttribute); 883 884 885 routeToBackendSet = new StringArgument(null, "routeToBackendSet", 886 false, 0, 887 INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_BACKEND_SET.get(), 888 INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_BACKEND_SET.get()); 889 routeToBackendSet.addLongIdentifier("route-to-backend-set", true); 890 routeToBackendSet.setArgumentGroupName( 891 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 892 parser.addArgument(routeToBackendSet); 893 894 895 routeToServer = new StringArgument(null, "routeToServer", false, 1, 896 INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_SERVER.get(), 897 INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_SERVER.get()); 898 routeToServer.addLongIdentifier("route-to-server", true); 899 routeToServer.setArgumentGroupName( 900 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 901 parser.addArgument(routeToServer); 902 903 904 assuredReplication = new BooleanArgument(null, "useAssuredReplication", 1, 905 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPLICATION.get( 906 ARG_ASSURED_REPLICATION_LOCAL_LEVEL, 907 ARG_ASSURED_REPLICATION_REMOTE_LEVEL, 908 ARG_ASSURED_REPLICATION_TIMEOUT)); 909 assuredReplication.addLongIdentifier("assuredReplication", true); 910 assuredReplication.addLongIdentifier("use-assured-replication", true); 911 assuredReplication.addLongIdentifier("assured-replication", true); 912 assuredReplication.setArgumentGroupName( 913 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 914 parser.addArgument(assuredReplication); 915 916 917 final Set<String> assuredReplicationLocalLevelAllowedValues = 918 StaticUtils.setOf("none", "received-any-server", 919 "processed-all-servers"); 920 assuredReplicationLocalLevel = new StringArgument(null, 921 ARG_ASSURED_REPLICATION_LOCAL_LEVEL, false, 1, 922 INFO_PLACEHOLDER_LEVEL.get(), 923 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_LOCAL_LEVEL.get( 924 assuredReplication.getIdentifierString()), 925 assuredReplicationLocalLevelAllowedValues); 926 assuredReplicationLocalLevel.addLongIdentifier( 927 "assured-replication-local-level", true); 928 assuredReplicationLocalLevel.setArgumentGroupName( 929 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 930 parser.addArgument(assuredReplicationLocalLevel); 931 932 933 final Set<String> assuredReplicationRemoteLevelAllowedValues = 934 StaticUtils.setOf("none", "received-any-remote-location", 935 "received-all-remote-locations", "processed-all-remote-servers"); 936 assuredReplicationRemoteLevel = new StringArgument(null, 937 ARG_ASSURED_REPLICATION_REMOTE_LEVEL, false, 1, 938 INFO_PLACEHOLDER_LEVEL.get(), 939 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_REMOTE_LEVEL.get( 940 assuredReplication.getIdentifierString()), 941 assuredReplicationRemoteLevelAllowedValues); 942 assuredReplicationRemoteLevel.addLongIdentifier( 943 "assured-replication-remote-level", true); 944 assuredReplicationRemoteLevel.setArgumentGroupName( 945 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 946 parser.addArgument(assuredReplicationRemoteLevel); 947 948 949 assuredReplicationTimeout = new DurationArgument(null, 950 ARG_ASSURED_REPLICATION_TIMEOUT, false, INFO_PLACEHOLDER_TIMEOUT.get(), 951 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_TIMEOUT.get( 952 assuredReplication.getIdentifierString())); 953 assuredReplicationTimeout.setArgumentGroupName( 954 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 955 parser.addArgument(assuredReplicationTimeout); 956 957 958 replicationRepair = new BooleanArgument(null, "replicationRepair", 959 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_REPLICATION_REPAIR.get()); 960 replicationRepair.addLongIdentifier("replication-repair", true); 961 replicationRepair.setArgumentGroupName( 962 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 963 parser.addArgument(replicationRepair); 964 965 966 nameWithEntryUUID = new BooleanArgument(null, "nameWithEntryUUID", 1, 967 INFO_LDAPMODIFY_ARG_DESCRIPTION_NAME_WITH_ENTRY_UUID.get()); 968 nameWithEntryUUID.addLongIdentifier("name-with-entryUUID", true); 969 nameWithEntryUUID.addLongIdentifier("name-with-entry-uuid", true); 970 nameWithEntryUUID.setArgumentGroupName( 971 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 972 parser.addArgument(nameWithEntryUUID); 973 974 975 noOperation = new BooleanArgument(null, "noOperation", 1, 976 INFO_LDAPMODIFY_ARG_DESCRIPTION_NO_OPERATION.get()); 977 noOperation.addLongIdentifier("noOp", true); 978 noOperation.addLongIdentifier("no-operation", true); 979 noOperation.addLongIdentifier("no-op", true); 980 noOperation.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 981 parser.addArgument(noOperation); 982 983 984 passwordUpdateBehavior = new StringArgument(null, 985 "passwordUpdateBehavior", false, 0, 986 INFO_LDAPMODIFY_PLACEHOLDER_NAME_EQUALS_VALUE.get(), 987 INFO_LDAPMODIFY_ARG_DESCRIPTION_PW_UPDATE_BEHAVIOR.get()); 988 passwordUpdateBehavior.addLongIdentifier("password-update-behavior", true); 989 passwordUpdateBehavior.setArgumentGroupName( 990 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 991 parser.addArgument(passwordUpdateBehavior); 992 993 passwordValidationDetails = new BooleanArgument(null, 994 "getPasswordValidationDetails", 1, 995 INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_VALIDATION_DETAILS.get( 996 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 997 passwordValidationDetails.addLongIdentifier("passwordValidationDetails", 998 true); 999 passwordValidationDetails.addLongIdentifier( 1000 "get-password-validation-details", true); 1001 passwordValidationDetails.addLongIdentifier("password-validation-details", 1002 true); 1003 passwordValidationDetails.setArgumentGroupName( 1004 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1005 parser.addArgument(passwordValidationDetails); 1006 1007 1008 permissiveModify = new BooleanArgument(null, "permissiveModify", 1009 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_PERMISSIVE_MODIFY.get()); 1010 permissiveModify.addLongIdentifier("permissive-modify", true); 1011 permissiveModify.setArgumentGroupName( 1012 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1013 parser.addArgument(permissiveModify); 1014 1015 1016 clientSideSubtreeDelete = new BooleanArgument(null, 1017 "clientSideSubtreeDelete", 1, 1018 INFO_LDAPMODIFY_ARG_DESCRIPTION_CLIENT_SIDE_SUBTREE_DELETE.get()); 1019 clientSideSubtreeDelete.addLongIdentifier("client-side-subtree-delete", 1020 true); 1021 clientSideSubtreeDelete.setArgumentGroupName( 1022 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1023 parser.addArgument(clientSideSubtreeDelete); 1024 1025 1026 serverSideSubtreeDelete = new BooleanArgument(null, 1027 "serverSideSubtreeDelete", 1, 1028 INFO_LDAPMODIFY_ARG_DESCRIPTION_SERVER_SIDE_SUBTREE_DELETE.get()); 1029 serverSideSubtreeDelete.addLongIdentifier("server-side-subtree-delete", 1030 true); 1031 serverSideSubtreeDelete.addLongIdentifier("subtreeDelete", true); 1032 serverSideSubtreeDelete.addLongIdentifier("subtree-delete", true); 1033 serverSideSubtreeDelete.addLongIdentifier("subtreeDeleteControl", true); 1034 serverSideSubtreeDelete.addLongIdentifier("subtree-delete-control", true); 1035 serverSideSubtreeDelete.addLongIdentifier("useSubtreeDeleteControl", true); 1036 serverSideSubtreeDelete.addLongIdentifier("use-subtree-delete-control", 1037 true); 1038 serverSideSubtreeDelete.setArgumentGroupName( 1039 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1040 parser.addArgument(serverSideSubtreeDelete); 1041 1042 1043 softDelete = new BooleanArgument('s', "softDelete", 1, 1044 INFO_LDAPMODIFY_ARG_DESCRIPTION_SOFT_DELETE.get()); 1045 softDelete.addLongIdentifier("useSoftDelete", true); 1046 softDelete.addLongIdentifier("soft-delete", true); 1047 softDelete.addLongIdentifier("use-soft-delete", true); 1048 softDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1049 parser.addArgument(softDelete); 1050 1051 1052 hardDelete = new BooleanArgument(null, "hardDelete", 1, 1053 INFO_LDAPMODIFY_ARG_DESCRIPTION_HARD_DELETE.get()); 1054 hardDelete.addLongIdentifier("hard-delete", true); 1055 hardDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1056 parser.addArgument(hardDelete); 1057 1058 1059 allowUndelete = new BooleanArgument(null, "allowUndelete", 1, 1060 INFO_LDAPMODIFY_ARG_DESCRIPTION_ALLOW_UNDELETE.get( 1061 ATTR_UNDELETE_FROM_DN)); 1062 allowUndelete.addLongIdentifier("allow-undelete", true); 1063 allowUndelete.setArgumentGroupName( 1064 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1065 parser.addArgument(allowUndelete); 1066 1067 1068 retireCurrentPassword = new BooleanArgument(null, "retireCurrentPassword", 1069 1, 1070 INFO_LDAPMODIFY_ARG_DESCRIPTION_RETIRE_CURRENT_PASSWORD.get( 1071 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 1072 retireCurrentPassword.addLongIdentifier("retire-current-password", true); 1073 retireCurrentPassword.setArgumentGroupName( 1074 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1075 parser.addArgument(retireCurrentPassword); 1076 1077 1078 purgeCurrentPassword = new BooleanArgument(null, "purgeCurrentPassword", 1, 1079 INFO_LDAPMODIFY_ARG_DESCRIPTION_PURGE_CURRENT_PASSWORD.get( 1080 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 1081 purgeCurrentPassword.addLongIdentifier("purge-current-password", true); 1082 purgeCurrentPassword.setArgumentGroupName( 1083 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1084 parser.addArgument(purgeCurrentPassword); 1085 1086 1087 final Set<String> suppressOperationalAttributeUpdatesAllowedValues = 1088 StaticUtils.setOf("last-access-time", "last-login-time", 1089 "last-login-ip", "lastmod"); 1090 suppressOperationalAttributeUpdates = new StringArgument(null, 1091 "suppressOperationalAttributeUpdates", false, -1, 1092 INFO_PLACEHOLDER_ATTR.get(), 1093 INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_OP_ATTR_UPDATES.get(), 1094 suppressOperationalAttributeUpdatesAllowedValues); 1095 suppressOperationalAttributeUpdates.addLongIdentifier( 1096 "suppress-operational-attribute-updates", true); 1097 suppressOperationalAttributeUpdates.setArgumentGroupName( 1098 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1099 parser.addArgument(suppressOperationalAttributeUpdates); 1100 1101 1102 suppressReferentialIntegrityUpdates = new BooleanArgument(null, 1103 "suppressReferentialIntegrityUpdates", 1, 1104 INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_REFERINT_UPDATES.get()); 1105 suppressReferentialIntegrityUpdates.addLongIdentifier( 1106 "suppress-referential-integrity-updates", true); 1107 suppressReferentialIntegrityUpdates.setArgumentGroupName( 1108 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1109 parser.addArgument(suppressReferentialIntegrityUpdates); 1110 1111 1112 usePasswordPolicyControl = new BooleanArgument(null, 1113 "usePasswordPolicyControl", 1, 1114 INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_POLICY.get()); 1115 usePasswordPolicyControl.addLongIdentifier("use-password-policy-control", 1116 true); 1117 usePasswordPolicyControl.setArgumentGroupName( 1118 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1119 parser.addArgument(usePasswordPolicyControl); 1120 1121 1122 uniquenessAttribute = new StringArgument(null, "uniquenessAttribute", false, 1123 0, INFO_PLACEHOLDER_ATTR.get(), 1124 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_ATTR.get()); 1125 uniquenessAttribute.addLongIdentifier("uniquenessAttributeType", true); 1126 uniquenessAttribute.addLongIdentifier("uniqueAttribute", true); 1127 uniquenessAttribute.addLongIdentifier("uniqueAttributeType", true); 1128 uniquenessAttribute.addLongIdentifier("uniqueness-attribute", true); 1129 uniquenessAttribute.addLongIdentifier("uniqueness-attribute-type", true); 1130 uniquenessAttribute.addLongIdentifier("unique-attribute", true); 1131 uniquenessAttribute.addLongIdentifier("unique-attribute-type", true); 1132 uniquenessAttribute.setArgumentGroupName( 1133 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1134 parser.addArgument(uniquenessAttribute); 1135 1136 1137 uniquenessFilter = new FilterArgument(null, "uniquenessFilter", false, 1, 1138 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_FILTER.get()); 1139 uniquenessFilter.addLongIdentifier("uniqueness-filter", true); 1140 uniquenessFilter.setArgumentGroupName( 1141 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1142 parser.addArgument(uniquenessFilter); 1143 1144 1145 uniquenessBaseDN = new DNArgument(null, "uniquenessBaseDN", false, 1, null, 1146 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_BASE_DN.get()); 1147 uniquenessBaseDN.addLongIdentifier("uniqueness-base-dn", true); 1148 uniquenessBaseDN.setArgumentGroupName( 1149 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1150 parser.addArgument(uniquenessBaseDN); 1151 parser.addDependentArgumentSet(uniquenessBaseDN, uniquenessAttribute, 1152 uniquenessFilter); 1153 1154 1155 final Set<String> mabValues = StaticUtils.setOf( 1156 "unique-within-each-attribute", 1157 "unique-across-all-attributes-including-in-same-entry", 1158 "unique-across-all-attributes-except-in-same-entry", 1159 "unique-in-combination"); 1160 uniquenessMultipleAttributeBehavior = new StringArgument(null, 1161 "uniquenessMultipleAttributeBehavior", false, 1, 1162 INFO_LDAPMODIFY_PLACEHOLDER_BEHAVIOR.get(), 1163 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_MULTIPLE_ATTRIBUTE_BEHAVIOR. 1164 get(), 1165 mabValues); 1166 uniquenessMultipleAttributeBehavior.addLongIdentifier( 1167 "uniqueness-multiple-attribute-behavior", true); 1168 uniquenessMultipleAttributeBehavior.setArgumentGroupName( 1169 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1170 parser.addArgument(uniquenessMultipleAttributeBehavior); 1171 parser.addDependentArgumentSet(uniquenessMultipleAttributeBehavior, 1172 uniquenessAttribute); 1173 1174 1175 final Set<String> vlValues = StaticUtils.setOf("none", "all-subtree-views", 1176 "all-backend-sets", "all-available-backend-servers"); 1177 uniquenessPreCommitValidationLevel = new StringArgument(null, 1178 "uniquenessPreCommitValidationLevel", false, 1, 1179 INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(), 1180 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_PRE_COMMIT_LEVEL.get(), 1181 vlValues); 1182 uniquenessPreCommitValidationLevel.addLongIdentifier( 1183 "uniqueness-pre-commit-validation-level", true); 1184 uniquenessPreCommitValidationLevel.setArgumentGroupName( 1185 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1186 parser.addArgument(uniquenessPreCommitValidationLevel); 1187 parser.addDependentArgumentSet(uniquenessPreCommitValidationLevel, 1188 uniquenessAttribute, uniquenessFilter); 1189 1190 1191 uniquenessPostCommitValidationLevel = new StringArgument(null, 1192 "uniquenessPostCommitValidationLevel", false, 1, 1193 INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(), 1194 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_POST_COMMIT_LEVEL.get(), 1195 vlValues); 1196 uniquenessPostCommitValidationLevel.addLongIdentifier( 1197 "uniqueness-post-commit-validation-level", true); 1198 uniquenessPostCommitValidationLevel.setArgumentGroupName( 1199 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1200 parser.addArgument(uniquenessPostCommitValidationLevel); 1201 parser.addDependentArgumentSet(uniquenessPostCommitValidationLevel, 1202 uniquenessAttribute, uniquenessFilter); 1203 1204 operationControl = new ControlArgument('J', "control", false, 0, null, 1205 INFO_LDAPMODIFY_ARG_DESCRIPTION_OP_CONTROL.get()); 1206 operationControl.setArgumentGroupName( 1207 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1208 parser.addArgument(operationControl); 1209 1210 1211 addControl = new ControlArgument(null, "addControl", false, 0, null, 1212 INFO_LDAPMODIFY_ARG_DESCRIPTION_ADD_CONTROL.get()); 1213 addControl.addLongIdentifier("add-control", true); 1214 addControl.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1215 parser.addArgument(addControl); 1216 1217 1218 bindControl = new ControlArgument(null, "bindControl", false, 0, null, 1219 INFO_LDAPMODIFY_ARG_DESCRIPTION_BIND_CONTROL.get()); 1220 bindControl.addLongIdentifier("bind-control", true); 1221 bindControl.setArgumentGroupName( 1222 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1223 parser.addArgument(bindControl); 1224 1225 1226 deleteControl = new ControlArgument(null, "deleteControl", false, 0, null, 1227 INFO_LDAPMODIFY_ARG_DESCRIPTION_DELETE_CONTROL.get()); 1228 deleteControl.addLongIdentifier("delete-control", true); 1229 deleteControl.setArgumentGroupName( 1230 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1231 parser.addArgument(deleteControl); 1232 1233 1234 modifyControl = new ControlArgument(null, "modifyControl", false, 0, null, 1235 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_CONTROL.get()); 1236 modifyControl.addLongIdentifier("modify-control", true); 1237 modifyControl.setArgumentGroupName( 1238 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1239 parser.addArgument(modifyControl); 1240 1241 1242 modifyDNControl = new ControlArgument(null, "modifyDNControl", false, 0, 1243 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_CONTROL.get()); 1244 modifyDNControl.addLongIdentifier("modify-dn-control", true); 1245 modifyDNControl.setArgumentGroupName( 1246 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1247 parser.addArgument(modifyDNControl); 1248 1249 1250 ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1, 1251 INFO_PLACEHOLDER_NUM.get(), 1252 INFO_LDAPMODIFY_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1, 1253 Integer.MAX_VALUE); 1254 ratePerSecond.addLongIdentifier("rate-per-second", true); 1255 ratePerSecond.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 1256 parser.addArgument(ratePerSecond); 1257 1258 1259 // The "--scriptFriendly" argument is provided for compatibility with legacy 1260 // ldapmodify tools, but is not actually used by this tool. 1261 final BooleanArgument scriptFriendly = new BooleanArgument(null, 1262 "scriptFriendly", 1, 1263 INFO_LDAPMODIFY_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get()); 1264 scriptFriendly.addLongIdentifier("script-friendly", true); 1265 scriptFriendly.setArgumentGroupName( 1266 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 1267 scriptFriendly.setHidden(true); 1268 parser.addArgument(scriptFriendly); 1269 1270 1271 // The "-V" / "--ldapVersion" argument is provided for compatibility with 1272 // legacy ldapmodify tools, but is not actually used by this tool. 1273 final IntegerArgument ldapVersion = new IntegerArgument('V', "ldapVersion", 1274 false, 1, null, INFO_LDAPMODIFY_ARG_DESCRIPTION_LDAP_VERSION.get()); 1275 ldapVersion.addLongIdentifier("ldap-version", true); 1276 ldapVersion.setHidden(true); 1277 parser.addArgument(ldapVersion); 1278 1279 1280 // A few assured replication arguments will only be allowed if assured 1281 // replication is to be used. 1282 parser.addDependentArgumentSet(assuredReplicationLocalLevel, 1283 assuredReplication); 1284 parser.addDependentArgumentSet(assuredReplicationRemoteLevel, 1285 assuredReplication); 1286 parser.addDependentArgumentSet(assuredReplicationTimeout, 1287 assuredReplication); 1288 1289 // Transactions will be incompatible with a lot of settings. 1290 parser.addExclusiveArgumentSet(useTransaction, multiUpdateErrorBehavior); 1291 parser.addExclusiveArgumentSet(useTransaction, rejectFile); 1292 parser.addExclusiveArgumentSet(useTransaction, retryFailedOperations); 1293 parser.addExclusiveArgumentSet(useTransaction, continueOnError); 1294 parser.addExclusiveArgumentSet(useTransaction, dryRun); 1295 parser.addExclusiveArgumentSet(useTransaction, followReferrals); 1296 parser.addExclusiveArgumentSet(useTransaction, nameWithEntryUUID); 1297 parser.addExclusiveArgumentSet(useTransaction, noOperation); 1298 parser.addExclusiveArgumentSet(useTransaction, modifyEntriesMatchingFilter); 1299 parser.addExclusiveArgumentSet(useTransaction, 1300 modifyEntriesMatchingFiltersFromFile); 1301 parser.addExclusiveArgumentSet(useTransaction, modifyEntryWithDN); 1302 parser.addExclusiveArgumentSet(useTransaction, 1303 modifyEntriesWithDNsFromFile); 1304 parser.addExclusiveArgumentSet(useTransaction, 1305 clientSideSubtreeDelete); 1306 1307 // Multi-update is incompatible with a lot of settings. 1308 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, ratePerSecond); 1309 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, rejectFile); 1310 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1311 retryFailedOperations); 1312 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, continueOnError); 1313 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, dryRun); 1314 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, followReferrals); 1315 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, nameWithEntryUUID); 1316 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, noOperation); 1317 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1318 modifyEntriesMatchingFilter); 1319 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1320 modifyEntriesMatchingFiltersFromFile); 1321 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, modifyEntryWithDN); 1322 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1323 modifyEntriesWithDNsFromFile); 1324 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1325 clientSideSubtreeDelete); 1326 1327 // Client-side and server-side subtree deletes cannot be used together. 1328 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, 1329 serverSideSubtreeDelete); 1330 1331 // Soft delete cannot be used with either hard delete or subtree delete. 1332 parser.addExclusiveArgumentSet(softDelete, hardDelete); 1333 parser.addExclusiveArgumentSet(softDelete, clientSideSubtreeDelete); 1334 parser.addExclusiveArgumentSet(softDelete, serverSideSubtreeDelete); 1335 1336 // Client-side subtree delete cannot be used in conjunction with a few 1337 // other settings. 1338 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, followReferrals); 1339 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, preReadAttribute); 1340 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getBackendSetID); 1341 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getServerID); 1342 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, noOperation); 1343 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, dryRun); 1344 1345 // Password retiring and purging can't be used together. 1346 parser.addExclusiveArgumentSet(retireCurrentPassword, purgeCurrentPassword); 1347 1348 // Referral following cannot be used in conjunction with the manageDsaIT 1349 // control. 1350 parser.addExclusiveArgumentSet(followReferrals, manageDsaIT); 1351 1352 // The proxyAs and proxyV1As arguments cannot be used together. 1353 parser.addExclusiveArgumentSet(proxyAs, proxyV1As); 1354 1355 // The modifyEntriesMatchingFilter argument is incompatible with a lot of 1356 // settings, since it can only be used for modify operations. 1357 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, allowUndelete); 1358 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, defaultAdd); 1359 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, dryRun); 1360 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, hardDelete); 1361 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1362 ignoreNoUserModification); 1363 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1364 nameWithEntryUUID); 1365 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, softDelete); 1366 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1367 clientSideSubtreeDelete); 1368 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1369 serverSideSubtreeDelete); 1370 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1371 suppressReferentialIntegrityUpdates); 1372 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, addControl); 1373 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, deleteControl); 1374 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1375 modifyDNControl); 1376 1377 // The modifyEntriesMatchingFilterFromFile argument is incompatible with a 1378 // lot of settings, since it can only be used for modify operations. 1379 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1380 allowUndelete); 1381 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1382 defaultAdd); 1383 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1384 dryRun); 1385 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1386 hardDelete); 1387 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1388 ignoreNoUserModification); 1389 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1390 nameWithEntryUUID); 1391 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1392 softDelete); 1393 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1394 clientSideSubtreeDelete); 1395 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1396 serverSideSubtreeDelete); 1397 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1398 suppressReferentialIntegrityUpdates); 1399 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1400 addControl); 1401 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1402 deleteControl); 1403 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1404 modifyDNControl); 1405 1406 // The modifyEntryWithDN argument is incompatible with a lot of 1407 // settings, since it can only be used for modify operations. 1408 parser.addExclusiveArgumentSet(modifyEntryWithDN, allowUndelete); 1409 parser.addExclusiveArgumentSet(modifyEntryWithDN, defaultAdd); 1410 parser.addExclusiveArgumentSet(modifyEntryWithDN, dryRun); 1411 parser.addExclusiveArgumentSet(modifyEntryWithDN, hardDelete); 1412 parser.addExclusiveArgumentSet(modifyEntryWithDN, ignoreNoUserModification); 1413 parser.addExclusiveArgumentSet(modifyEntryWithDN, nameWithEntryUUID); 1414 parser.addExclusiveArgumentSet(modifyEntryWithDN, softDelete); 1415 parser.addExclusiveArgumentSet(modifyEntryWithDN, clientSideSubtreeDelete); 1416 parser.addExclusiveArgumentSet(modifyEntryWithDN, serverSideSubtreeDelete); 1417 parser.addExclusiveArgumentSet(modifyEntryWithDN, 1418 suppressReferentialIntegrityUpdates); 1419 parser.addExclusiveArgumentSet(modifyEntryWithDN, addControl); 1420 parser.addExclusiveArgumentSet(modifyEntryWithDN, deleteControl); 1421 parser.addExclusiveArgumentSet(modifyEntryWithDN, modifyDNControl); 1422 1423 // The modifyEntriesWithDNsFromFile argument is incompatible with a lot of 1424 // settings, since it can only be used for modify operations. 1425 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, allowUndelete); 1426 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, defaultAdd); 1427 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, dryRun); 1428 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, hardDelete); 1429 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1430 ignoreNoUserModification); 1431 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1432 nameWithEntryUUID); 1433 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, softDelete); 1434 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1435 clientSideSubtreeDelete); 1436 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1437 serverSideSubtreeDelete); 1438 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1439 suppressReferentialIntegrityUpdates); 1440 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, addControl); 1441 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, deleteControl); 1442 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1443 modifyDNControl); 1444 } 1445 1446 1447 1448 /** 1449 * {@inheritDoc} 1450 */ 1451 @Override() 1452 public void doExtendedNonLDAPArgumentValidation() 1453 throws ArgumentException 1454 { 1455 // If we should use the route to backend set request control, then validate 1456 // and pre-create those controls. 1457 if (routeToBackendSet.isPresent()) 1458 { 1459 final List<String> values = routeToBackendSet.getValues(); 1460 final Map<String,List<String>> idsByRP = new LinkedHashMap<>( 1461 StaticUtils.computeMapCapacity(values.size())); 1462 for (final String value : values) 1463 { 1464 final int colonPos = value.indexOf(':'); 1465 if (colonPos <= 0) 1466 { 1467 throw new ArgumentException( 1468 ERR_LDAPMODIFY_ROUTE_TO_BACKEND_SET_INVALID_FORMAT.get(value, 1469 routeToBackendSet.getIdentifierString())); 1470 } 1471 1472 final String rpID = value.substring(0, colonPos); 1473 final String bsID = value.substring(colonPos+1); 1474 1475 List<String> idsForRP = idsByRP.get(rpID); 1476 if (idsForRP == null) 1477 { 1478 idsForRP = new ArrayList<>(values.size()); 1479 idsByRP.put(rpID, idsForRP); 1480 } 1481 idsForRP.add(bsID); 1482 } 1483 1484 for (final Map.Entry<String,List<String>> e : idsByRP.entrySet()) 1485 { 1486 final String rpID = e.getKey(); 1487 final List<String> bsIDs = e.getValue(); 1488 routeToBackendSetRequestControls.add( 1489 RouteToBackendSetRequestControl.createAbsoluteRoutingRequest(true, 1490 rpID, bsIDs)); 1491 } 1492 } 1493 } 1494 1495 1496 1497 /** 1498 * {@inheritDoc} 1499 */ 1500 @Override() 1501 protected List<Control> getBindControls() 1502 { 1503 final ArrayList<Control> bindControls = new ArrayList<>(10); 1504 1505 if (bindControl.isPresent()) 1506 { 1507 bindControls.addAll(bindControl.getValues()); 1508 } 1509 1510 if (authorizationIdentity.isPresent()) 1511 { 1512 bindControls.add(new AuthorizationIdentityRequestControl(false)); 1513 } 1514 1515 if (getAuthorizationEntryAttribute.isPresent()) 1516 { 1517 bindControls.add(new GetAuthorizationEntryRequestControl(true, true, 1518 getAuthorizationEntryAttribute.getValues())); 1519 } 1520 1521 if (getUserResourceLimits.isPresent()) 1522 { 1523 bindControls.add(new GetUserResourceLimitsRequestControl()); 1524 } 1525 1526 if (usePasswordPolicyControl.isPresent()) 1527 { 1528 bindControls.add(new PasswordPolicyRequestControl()); 1529 } 1530 1531 if (suppressOperationalAttributeUpdates.isPresent()) 1532 { 1533 final EnumSet<SuppressType> suppressTypes = 1534 EnumSet.noneOf(SuppressType.class); 1535 for (final String s : suppressOperationalAttributeUpdates.getValues()) 1536 { 1537 if (s.equalsIgnoreCase("last-access-time")) 1538 { 1539 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 1540 } 1541 else if (s.equalsIgnoreCase("last-login-time")) 1542 { 1543 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 1544 } 1545 else if (s.equalsIgnoreCase("last-login-ip")) 1546 { 1547 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 1548 } 1549 } 1550 1551 bindControls.add(new SuppressOperationalAttributeUpdateRequestControl( 1552 suppressTypes)); 1553 } 1554 1555 return bindControls; 1556 } 1557 1558 1559 1560 /** 1561 * {@inheritDoc} 1562 */ 1563 @Override() 1564 protected boolean supportsMultipleServers() 1565 { 1566 // We will support providing information about multiple servers. This tool 1567 // will not communicate with multiple servers concurrently, but it can 1568 // accept information about multiple servers in the event that a large set 1569 // of changes is to be processed and a server goes down in the middle of 1570 // those changes. In this case, we can resume processing on a newly-created 1571 // connection, possibly to a different server. 1572 return true; 1573 } 1574 1575 1576 1577 /** 1578 * {@inheritDoc} 1579 */ 1580 @Override() 1581 public LDAPConnectionOptions getConnectionOptions() 1582 { 1583 final LDAPConnectionOptions options = new LDAPConnectionOptions(); 1584 1585 options.setUseSynchronousMode(true); 1586 options.setFollowReferrals(followReferrals.isPresent()); 1587 options.setUnsolicitedNotificationHandler(this); 1588 options.setResponseTimeoutMillis(0L); 1589 1590 return options; 1591 } 1592 1593 1594 1595 /** 1596 * {@inheritDoc} 1597 */ 1598 @Override() 1599 public ResultCode doToolProcessing() 1600 { 1601 // Examine the arguments to determine the sets of controls to use for each 1602 // type of request. 1603 final ArrayList<Control> addControls = new ArrayList<>(10); 1604 final ArrayList<Control> deleteControls = new ArrayList<>(10); 1605 final ArrayList<Control> modifyControls = new ArrayList<>(10); 1606 final ArrayList<Control> modifyDNControls = new ArrayList<>(10); 1607 final ArrayList<Control> searchControls = new ArrayList<>(10); 1608 try 1609 { 1610 createRequestControls(addControls, deleteControls, modifyControls, 1611 modifyDNControls, searchControls); 1612 } 1613 catch (final LDAPException le) 1614 { 1615 Debug.debugException(le); 1616 for (final String line : 1617 ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) 1618 { 1619 err(line); 1620 } 1621 return le.getResultCode(); 1622 } 1623 1624 1625 // If an encryption passphrase file was specified, then read its value. 1626 String encryptionPassphrase = null; 1627 if (encryptionPassphraseFile.isPresent()) 1628 { 1629 try 1630 { 1631 encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile( 1632 encryptionPassphraseFile.getValue()); 1633 } 1634 catch (final LDAPException e) 1635 { 1636 Debug.debugException(e); 1637 wrapErr(0, WRAP_COLUMN, e.getMessage()); 1638 return e.getResultCode(); 1639 } 1640 } 1641 1642 1643 LDAPConnectionPool connectionPool = null; 1644 LDIFReader ldifReader = null; 1645 LDIFWriter rejectWriter = null; 1646 try 1647 { 1648 // Create a connection pool that will be used to communicate with the 1649 // directory server. If we should use an administrative session, then 1650 // create a connect processor that will be used to start the session 1651 // before performing the bind. 1652 try 1653 { 1654 final StartAdministrativeSessionPostConnectProcessor p; 1655 if (useAdministrativeSession.isPresent()) 1656 { 1657 p = new StartAdministrativeSessionPostConnectProcessor( 1658 new StartAdministrativeSessionExtendedRequest(getToolName(), 1659 true)); 1660 } 1661 else 1662 { 1663 p = null; 1664 } 1665 1666 if (! dryRun.isPresent()) 1667 { 1668 connectionPool = getConnectionPool(1, 2, 0, p, null, true, 1669 new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, 1670 verbose.isPresent())); 1671 } 1672 } 1673 catch (final LDAPException le) 1674 { 1675 Debug.debugException(le); 1676 1677 // Unable to create the connection pool, which means that either the 1678 // connection could not be established or the attempt to authenticate 1679 // the connection failed. If the bind failed, then the report bind 1680 // result health check should have already reported the bind failure. 1681 // If the failure was something else, then display that failure result. 1682 if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) 1683 { 1684 for (final String line : 1685 ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) 1686 { 1687 err(line); 1688 } 1689 } 1690 return le.getResultCode(); 1691 } 1692 1693 if ((connectionPool != null) && retryFailedOperations.isPresent()) 1694 { 1695 connectionPool.setRetryFailedOperationsDueToInvalidConnections(true); 1696 } 1697 1698 1699 // Report that the connection was successfully established. 1700 if (connectionPool != null) 1701 { 1702 try 1703 { 1704 final LDAPConnection connection = connectionPool.getConnection(); 1705 final String hostPort = connection.getHostPort(); 1706 connectionPool.releaseConnection(connection); 1707 commentToOut(INFO_LDAPMODIFY_CONNECTION_ESTABLISHED.get(hostPort)); 1708 out(); 1709 } 1710 catch (final LDAPException le) 1711 { 1712 Debug.debugException(le); 1713 // This should never happen. 1714 } 1715 } 1716 1717 1718 // If we should process the operations in a transaction, then start that 1719 // now. 1720 final ASN1OctetString txnID; 1721 if (useTransaction.isPresent()) 1722 { 1723 final Control[] startTxnControls; 1724 if (proxyAs.isPresent()) 1725 { 1726 // In a transaction, the proxied authorization control must only be 1727 // used in the start transaction request and not in any of the 1728 // subsequent operation requests. 1729 startTxnControls = new Control[] 1730 { 1731 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()) 1732 }; 1733 } 1734 else if (proxyV1As.isPresent()) 1735 { 1736 // In a transaction, the proxied authorization control must only be 1737 // used in the start transaction request and not in any of the 1738 // subsequent operation requests. 1739 startTxnControls = new Control[] 1740 { 1741 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()) 1742 }; 1743 } 1744 else 1745 { 1746 startTxnControls = StaticUtils.NO_CONTROLS; 1747 } 1748 1749 try 1750 { 1751 final StartTransactionExtendedResult startTxnResult = 1752 (StartTransactionExtendedResult) 1753 connectionPool.processExtendedOperation( 1754 new StartTransactionExtendedRequest(startTxnControls)); 1755 if (startTxnResult.getResultCode() == ResultCode.SUCCESS) 1756 { 1757 txnID = startTxnResult.getTransactionID(); 1758 1759 final TransactionSpecificationRequestControl c = 1760 new TransactionSpecificationRequestControl(txnID); 1761 addControls.add(c); 1762 deleteControls.add(c); 1763 modifyControls.add(c); 1764 modifyDNControls.add(c); 1765 1766 final String txnIDString; 1767 if (StaticUtils.isPrintableString(txnID.getValue())) 1768 { 1769 txnIDString = txnID.stringValue(); 1770 } 1771 else 1772 { 1773 final StringBuilder hexBuffer = new StringBuilder(); 1774 StaticUtils.toHex(txnID.getValue(), ":", hexBuffer); 1775 txnIDString = hexBuffer.toString(); 1776 } 1777 1778 commentToOut(INFO_LDAPMODIFY_STARTED_TXN.get(txnIDString)); 1779 } 1780 else 1781 { 1782 commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get( 1783 startTxnResult.getResultString())); 1784 return startTxnResult.getResultCode(); 1785 } 1786 } 1787 catch (final LDAPException le) 1788 { 1789 Debug.debugException(le); 1790 commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get( 1791 StaticUtils.getExceptionMessage(le))); 1792 return le.getResultCode(); 1793 } 1794 } 1795 else 1796 { 1797 txnID = null; 1798 } 1799 1800 1801 // Create an LDIF reader that will be used to read the changes to process. 1802 try 1803 { 1804 final InputStream ldifInputStream; 1805 if (ldifFile.isPresent()) 1806 { 1807 ldifInputStream = ToolUtils.getInputStreamForLDIFFiles( 1808 ldifFile.getValues(), encryptionPassphrase, getOut(), 1809 getErr()).getFirst(); 1810 } 1811 else 1812 { 1813 ldifInputStream = in; 1814 } 1815 1816 ldifReader = new LDIFReader(ldifInputStream, 0, null, null, 1817 characterSet.getValue()); 1818 } 1819 catch (final Exception e) 1820 { 1821 commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_LDIF_READER.get( 1822 StaticUtils.getExceptionMessage(e))); 1823 return ResultCode.LOCAL_ERROR; 1824 } 1825 1826 if (stripTrailingSpaces.isPresent()) 1827 { 1828 ldifReader.setTrailingSpaceBehavior(TrailingSpaceBehavior.STRIP); 1829 } 1830 1831 1832 // If appropriate, create a reject writer. 1833 if (rejectFile.isPresent()) 1834 { 1835 try 1836 { 1837 rejectWriter = new LDIFWriter(rejectFile.getValue()); 1838 1839 // Set the maximum allowed wrap column. This is better than setting a 1840 // wrap column of zero because it will ensure that comments don't get 1841 // wrapped either. 1842 rejectWriter.setWrapColumn(Integer.MAX_VALUE); 1843 } 1844 catch (final Exception e) 1845 { 1846 Debug.debugException(e); 1847 commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_REJECT_WRITER.get( 1848 rejectFile.getValue().getAbsolutePath(), 1849 StaticUtils.getExceptionMessage(e))); 1850 return ResultCode.LOCAL_ERROR; 1851 } 1852 } 1853 1854 1855 // If appropriate, create a rate limiter. 1856 final FixedRateBarrier rateLimiter; 1857 if (ratePerSecond.isPresent()) 1858 { 1859 rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue()); 1860 } 1861 else 1862 { 1863 rateLimiter = null; 1864 } 1865 1866 1867 // Iterate through the set of changes to process. 1868 boolean commitTransaction = true; 1869 ResultCode resultCode = null; 1870 final ArrayList<LDAPRequest> multiUpdateRequests = 1871 new ArrayList<>(10); 1872 final boolean isBulkModify = modifyEntriesMatchingFilter.isPresent() || 1873 modifyEntriesMatchingFiltersFromFile.isPresent() || 1874 modifyEntryWithDN.isPresent() || 1875 modifyEntriesWithDNsFromFile.isPresent(); 1876readChangeRecordLoop: 1877 while (true) 1878 { 1879 // If there is a rate limiter, then use it to sleep if necessary. 1880 if ((rateLimiter != null) && (! isBulkModify)) 1881 { 1882 rateLimiter.await(); 1883 } 1884 1885 1886 // Read the next LDIF change record. If we get an error then handle it 1887 // and abort if appropriate. 1888 final LDIFChangeRecord changeRecord; 1889 try 1890 { 1891 changeRecord = ldifReader.readChangeRecord(defaultAdd.isPresent()); 1892 } 1893 catch (final IOException ioe) 1894 { 1895 Debug.debugException(ioe); 1896 1897 final String message = ERR_LDAPMODIFY_IO_ERROR_READING_CHANGE.get( 1898 StaticUtils.getExceptionMessage(ioe)); 1899 commentToErr(message); 1900 writeRejectedChange(rejectWriter, message, null); 1901 commitTransaction = false; 1902 resultCode = ResultCode.LOCAL_ERROR; 1903 break; 1904 } 1905 catch (final LDIFException le) 1906 { 1907 Debug.debugException(le); 1908 1909 final StringBuilder buffer = new StringBuilder(); 1910 if (le.mayContinueReading() && (! useTransaction.isPresent())) 1911 { 1912 buffer.append( 1913 ERR_LDAPMODIFY_RECOVERABLE_LDIF_ERROR_READING_CHANGE.get( 1914 le.getLineNumber(), StaticUtils.getExceptionMessage(le))); 1915 } 1916 else 1917 { 1918 buffer.append( 1919 ERR_LDAPMODIFY_UNRECOVERABLE_LDIF_ERROR_READING_CHANGE.get( 1920 le.getLineNumber(), StaticUtils.getExceptionMessage(le))); 1921 } 1922 1923 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS)) 1924 { 1925 resultCode = ResultCode.LOCAL_ERROR; 1926 } 1927 1928 if ((le.getDataLines() != null) && (! le.getDataLines().isEmpty())) 1929 { 1930 buffer.append(StaticUtils.EOL); 1931 buffer.append(StaticUtils.EOL); 1932 buffer.append(ERR_LDAPMODIFY_INVALID_LINES.get()); 1933 buffer.append(StaticUtils.EOL); 1934 for (final String s : le.getDataLines()) 1935 { 1936 buffer.append(s); 1937 buffer.append(StaticUtils.EOL); 1938 } 1939 } 1940 1941 final String message = buffer.toString(); 1942 commentToErr(message); 1943 writeRejectedChange(rejectWriter, message, null); 1944 1945 if (le.mayContinueReading() && (! useTransaction.isPresent())) 1946 { 1947 continue; 1948 } 1949 else 1950 { 1951 commitTransaction = false; 1952 resultCode = ResultCode.LOCAL_ERROR; 1953 break; 1954 } 1955 } 1956 1957 1958 // If we read a null change record, then there are no more changes to 1959 // process. Otherwise, treat it appropriately based on the operation 1960 // type. 1961 if (changeRecord == null) 1962 { 1963 break; 1964 } 1965 1966 1967 // If we should modify entries matching a specified filter, then convert 1968 // the change record into a set of modifications. 1969 if (modifyEntriesMatchingFilter.isPresent()) 1970 { 1971 for (final Filter filter : modifyEntriesMatchingFilter.getValues()) 1972 { 1973 final ResultCode rc = handleModifyMatchingFilter(connectionPool, 1974 changeRecord, 1975 modifyEntriesMatchingFilter.getIdentifierString(), 1976 filter, searchControls, modifyControls, rateLimiter, 1977 rejectWriter); 1978 if (rc != ResultCode.SUCCESS) 1979 { 1980 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 1981 (resultCode == ResultCode.NO_OPERATION)) 1982 { 1983 resultCode = rc; 1984 } 1985 } 1986 } 1987 } 1988 1989 if (modifyEntriesMatchingFiltersFromFile.isPresent()) 1990 { 1991 for (final File f : modifyEntriesMatchingFiltersFromFile.getValues()) 1992 { 1993 final FilterFileReader filterReader; 1994 try 1995 { 1996 filterReader = new FilterFileReader(f); 1997 } 1998 catch (final Exception e) 1999 { 2000 Debug.debugException(e); 2001 commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_FILTER_FILE.get( 2002 f.getAbsolutePath(), StaticUtils.getExceptionMessage(e))); 2003 return ResultCode.LOCAL_ERROR; 2004 } 2005 2006 try 2007 { 2008 while (true) 2009 { 2010 final Filter filter; 2011 try 2012 { 2013 filter = filterReader.readFilter(); 2014 } 2015 catch (final IOException ioe) 2016 { 2017 Debug.debugException(ioe); 2018 commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_FILTER_FILE.get( 2019 f.getAbsolutePath(), 2020 StaticUtils.getExceptionMessage(ioe))); 2021 return ResultCode.LOCAL_ERROR; 2022 } 2023 catch (final LDAPException le) 2024 { 2025 Debug.debugException(le); 2026 commentToErr(le.getMessage()); 2027 if (continueOnError.isPresent()) 2028 { 2029 if ((resultCode == null) || 2030 (resultCode == ResultCode.SUCCESS) || 2031 (resultCode == ResultCode.NO_OPERATION)) 2032 { 2033 resultCode = le.getResultCode(); 2034 } 2035 continue; 2036 } 2037 else 2038 { 2039 return le.getResultCode(); 2040 } 2041 } 2042 2043 if (filter == null) 2044 { 2045 break; 2046 } 2047 2048 final ResultCode rc = handleModifyMatchingFilter(connectionPool, 2049 changeRecord, 2050 modifyEntriesMatchingFiltersFromFile.getIdentifierString(), 2051 filter, searchControls, modifyControls, rateLimiter, 2052 rejectWriter); 2053 if (rc != ResultCode.SUCCESS) 2054 { 2055 if ((resultCode == null) || 2056 (resultCode == ResultCode.SUCCESS) || 2057 (resultCode == ResultCode.NO_OPERATION)) 2058 { 2059 resultCode = rc; 2060 } 2061 } 2062 } 2063 } 2064 finally 2065 { 2066 try 2067 { 2068 filterReader.close(); 2069 } 2070 catch (final Exception e) 2071 { 2072 Debug.debugException(e); 2073 } 2074 } 2075 } 2076 } 2077 2078 if (modifyEntryWithDN.isPresent()) 2079 { 2080 for (final DN dn : modifyEntryWithDN.getValues()) 2081 { 2082 final ResultCode rc = handleModifyWithDN(connectionPool, 2083 changeRecord, modifyEntryWithDN.getIdentifierString(), dn, 2084 modifyControls, rateLimiter, rejectWriter); 2085 if (rc != ResultCode.SUCCESS) 2086 { 2087 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 2088 (resultCode == ResultCode.NO_OPERATION)) 2089 { 2090 resultCode = rc; 2091 } 2092 } 2093 } 2094 } 2095 2096 if (modifyEntriesWithDNsFromFile.isPresent()) 2097 { 2098 for (final File f : modifyEntriesWithDNsFromFile.getValues()) 2099 { 2100 final DNFileReader dnReader; 2101 try 2102 { 2103 dnReader = new DNFileReader(f); 2104 } 2105 catch (final Exception e) 2106 { 2107 Debug.debugException(e); 2108 commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_DN_FILE.get( 2109 f.getAbsolutePath(), StaticUtils.getExceptionMessage(e))); 2110 return ResultCode.LOCAL_ERROR; 2111 } 2112 2113 try 2114 { 2115 while (true) 2116 { 2117 final DN dn; 2118 try 2119 { 2120 dn = dnReader.readDN(); 2121 } 2122 catch (final IOException ioe) 2123 { 2124 Debug.debugException(ioe); 2125 commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_DN_FILE.get( 2126 f.getAbsolutePath(), 2127 StaticUtils.getExceptionMessage(ioe))); 2128 return ResultCode.LOCAL_ERROR; 2129 } 2130 catch (final LDAPException le) 2131 { 2132 Debug.debugException(le); 2133 commentToErr(le.getMessage()); 2134 if (continueOnError.isPresent()) 2135 { 2136 if ((resultCode == null) || 2137 (resultCode == ResultCode.SUCCESS) || 2138 (resultCode == ResultCode.NO_OPERATION)) 2139 { 2140 resultCode = le.getResultCode(); 2141 } 2142 continue; 2143 } 2144 else 2145 { 2146 return le.getResultCode(); 2147 } 2148 } 2149 2150 if (dn == null) 2151 { 2152 break; 2153 } 2154 2155 final ResultCode rc = handleModifyWithDN(connectionPool, 2156 changeRecord, 2157 modifyEntriesWithDNsFromFile.getIdentifierString(), dn, 2158 modifyControls, rateLimiter, rejectWriter); 2159 if (rc != ResultCode.SUCCESS) 2160 { 2161 if ((resultCode == null) || 2162 (resultCode == ResultCode.SUCCESS) || 2163 (resultCode == ResultCode.NO_OPERATION)) 2164 { 2165 resultCode = rc; 2166 } 2167 } 2168 } 2169 } 2170 finally 2171 { 2172 try 2173 { 2174 dnReader.close(); 2175 } 2176 catch (final Exception e) 2177 { 2178 Debug.debugException(e); 2179 } 2180 } 2181 } 2182 } 2183 2184 if (isBulkModify) 2185 { 2186 continue; 2187 } 2188 2189 try 2190 { 2191 final ResultCode rc; 2192 if (changeRecord instanceof LDIFAddChangeRecord) 2193 { 2194 rc = doAdd((LDIFAddChangeRecord) changeRecord, addControls, 2195 connectionPool, multiUpdateRequests, rejectWriter); 2196 } 2197 else if (changeRecord instanceof LDIFDeleteChangeRecord) 2198 { 2199 rc = doDelete((LDIFDeleteChangeRecord) changeRecord, deleteControls, 2200 connectionPool, multiUpdateRequests, rejectWriter); 2201 } 2202 else if (changeRecord instanceof LDIFModifyChangeRecord) 2203 { 2204 rc = doModify((LDIFModifyChangeRecord) changeRecord, modifyControls, 2205 connectionPool, multiUpdateRequests, rejectWriter); 2206 } 2207 else if (changeRecord instanceof LDIFModifyDNChangeRecord) 2208 { 2209 rc = doModifyDN((LDIFModifyDNChangeRecord) changeRecord, 2210 modifyDNControls, connectionPool, multiUpdateRequests, 2211 rejectWriter); 2212 } 2213 else 2214 { 2215 // This should never happen. 2216 commentToErr(ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get()); 2217 for (final String line : changeRecord.toLDIF()) 2218 { 2219 err("# " + line); 2220 } 2221 throw new LDAPException(ResultCode.PARAM_ERROR, 2222 ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get() + 2223 changeRecord.toString()); 2224 } 2225 2226 if ((resultCode == null) && (rc != ResultCode.SUCCESS)) 2227 { 2228 resultCode = rc; 2229 } 2230 } 2231 catch (final LDAPException le) 2232 { 2233 Debug.debugException(le); 2234 2235 commitTransaction = false; 2236 if (continueOnError.isPresent()) 2237 { 2238 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 2239 (resultCode == ResultCode.NO_OPERATION)) 2240 { 2241 resultCode = le.getResultCode(); 2242 } 2243 } 2244 else 2245 { 2246 resultCode = le.getResultCode(); 2247 break; 2248 } 2249 } 2250 } 2251 2252 2253 // If the operations are part of a transaction, then commit or abort that 2254 // transaction now. Otherwise, if they should be part of a multi-update 2255 // operation, then process that now. 2256 if (useTransaction.isPresent()) 2257 { 2258 LDAPResult endTxnResult; 2259 final EndTransactionExtendedRequest endTxnRequest = 2260 new EndTransactionExtendedRequest(txnID, commitTransaction); 2261 try 2262 { 2263 endTxnResult = connectionPool.processExtendedOperation(endTxnRequest); 2264 } 2265 catch (final LDAPException le) 2266 { 2267 endTxnResult = le.toLDAPResult(); 2268 } 2269 2270 displayResult(endTxnResult, false); 2271 if (((resultCode == null) || (resultCode == ResultCode.SUCCESS)) && 2272 (endTxnResult.getResultCode() != ResultCode.SUCCESS)) 2273 { 2274 resultCode = endTxnResult.getResultCode(); 2275 } 2276 } 2277 else if (multiUpdateErrorBehavior.isPresent()) 2278 { 2279 final MultiUpdateErrorBehavior errorBehavior; 2280 if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase("atomic")) 2281 { 2282 errorBehavior = MultiUpdateErrorBehavior.ATOMIC; 2283 } 2284 else if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase( 2285 "abort-on-error")) 2286 { 2287 errorBehavior = MultiUpdateErrorBehavior.ABORT_ON_ERROR; 2288 } 2289 else 2290 { 2291 errorBehavior = MultiUpdateErrorBehavior.CONTINUE_ON_ERROR; 2292 } 2293 2294 final Control[] multiUpdateControls; 2295 if (proxyAs.isPresent()) 2296 { 2297 multiUpdateControls = new Control[] 2298 { 2299 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()) 2300 }; 2301 } 2302 else if (proxyV1As.isPresent()) 2303 { 2304 multiUpdateControls = new Control[] 2305 { 2306 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()) 2307 }; 2308 } 2309 else 2310 { 2311 multiUpdateControls = StaticUtils.NO_CONTROLS; 2312 } 2313 2314 ExtendedResult multiUpdateResult; 2315 try 2316 { 2317 commentToOut(INFO_LDAPMODIFY_SENDING_MULTI_UPDATE_REQUEST.get()); 2318 final MultiUpdateExtendedRequest multiUpdateRequest = 2319 new MultiUpdateExtendedRequest(errorBehavior, 2320 multiUpdateRequests, multiUpdateControls); 2321 multiUpdateResult = 2322 connectionPool.processExtendedOperation(multiUpdateRequest); 2323 } 2324 catch (final LDAPException le) 2325 { 2326 multiUpdateResult = new ExtendedResult(le); 2327 } 2328 2329 displayResult(multiUpdateResult, false); 2330 resultCode = multiUpdateResult.getResultCode(); 2331 } 2332 2333 2334 if (resultCode == null) 2335 { 2336 return ResultCode.SUCCESS; 2337 } 2338 else 2339 { 2340 return resultCode; 2341 } 2342 } 2343 finally 2344 { 2345 if (rejectWriter != null) 2346 { 2347 try 2348 { 2349 rejectWriter.close(); 2350 } 2351 catch (final Exception e) 2352 { 2353 Debug.debugException(e); 2354 } 2355 } 2356 2357 if (ldifReader != null) 2358 { 2359 try 2360 { 2361 ldifReader.close(); 2362 } 2363 catch (final Exception e) 2364 { 2365 Debug.debugException(e); 2366 } 2367 } 2368 2369 if (connectionPool != null) 2370 { 2371 try 2372 { 2373 connectionPool.close(); 2374 } 2375 catch (final Exception e) 2376 { 2377 Debug.debugException(e); 2378 } 2379 } 2380 } 2381 } 2382 2383 2384 2385 /** 2386 * Handles the processing for a change record when the tool should modify 2387 * entries matching a given filter. 2388 * 2389 * @param connectionPool The connection pool to use to communicate with 2390 * the directory server. 2391 * @param changeRecord The LDIF change record to be processed. 2392 * @param argIdentifierString The identifier string for the argument used to 2393 * specify the filter to use to identify the 2394 * entries to modify. 2395 * @param filter The filter to use to identify the entries to 2396 * modify. 2397 * @param searchControls The set of controls to include in the search 2398 * request. 2399 * @param modifyControls The set of controls to include in the modify 2400 * requests. 2401 * @param rateLimiter The fixed-rate barrier to use for rate 2402 * limiting. It may be {@code null} if no rate 2403 * limiting is required. 2404 * @param rejectWriter The reject writer to use to record information 2405 * about any failed operations. 2406 * 2407 * @return A result code obtained from processing. 2408 */ 2409 private ResultCode handleModifyMatchingFilter( 2410 final LDAPConnectionPool connectionPool, 2411 final LDIFChangeRecord changeRecord, 2412 final String argIdentifierString, final Filter filter, 2413 final List<Control> searchControls, 2414 final List<Control> modifyControls, 2415 final FixedRateBarrier rateLimiter, 2416 final LDIFWriter rejectWriter) 2417 { 2418 // If the provided change record isn't a modify change record, then that's 2419 // an error. Reject it. 2420 if (! (changeRecord instanceof LDIFModifyChangeRecord)) 2421 { 2422 writeRejectedChange(rejectWriter, 2423 ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString), 2424 changeRecord); 2425 return ResultCode.PARAM_ERROR; 2426 } 2427 2428 final LDIFModifyChangeRecord modifyChangeRecord = 2429 (LDIFModifyChangeRecord) changeRecord; 2430 final HashSet<DN> processedDNs = 2431 new HashSet<>(StaticUtils.computeMapCapacity(100)); 2432 2433 2434 // If we need to use the simple paged results control, then we may have to 2435 // issue multiple searches. 2436 ASN1OctetString pagedResultsCookie = null; 2437 long entriesProcessed = 0L; 2438 ResultCode resultCode = ResultCode.SUCCESS; 2439 while (true) 2440 { 2441 // Construct the search request to send. 2442 final LDAPModifySearchListener listener = 2443 new LDAPModifySearchListener(this, modifyChangeRecord, filter, 2444 modifyControls, connectionPool, rateLimiter, rejectWriter, 2445 processedDNs); 2446 2447 final SearchRequest searchRequest = 2448 new SearchRequest(listener, modifyChangeRecord.getDN(), 2449 SearchScope.SUB, filter, SearchRequest.NO_ATTRIBUTES); 2450 searchRequest.setControls(searchControls); 2451 if (searchPageSize.isPresent()) 2452 { 2453 searchRequest.addControl(new SimplePagedResultsControl( 2454 searchPageSize.getValue(), pagedResultsCookie)); 2455 } 2456 2457 2458 // The connection pool's automatic retry feature can't work for searches 2459 // that return one or more entries before encountering a failure. To get 2460 // around that, we'll check a connection out of the pool and use it to 2461 // process the search. If an error occurs that indicates the connection 2462 // is no longer valid, we can replace it with a newly-established 2463 // connection and try again. The search result listener will ensure that 2464 // no entry gets updated twice. 2465 LDAPConnection connection; 2466 try 2467 { 2468 connection = connectionPool.getConnection(); 2469 } 2470 catch (final LDAPException le) 2471 { 2472 Debug.debugException(le); 2473 2474 writeRejectedChange(rejectWriter, 2475 ERR_LDAPMODIFY_CANNOT_GET_SEARCH_CONNECTION.get( 2476 modifyChangeRecord.getDN(), String.valueOf(filter), 2477 StaticUtils.getExceptionMessage(le)), 2478 modifyChangeRecord, le.toLDAPResult()); 2479 return le.getResultCode(); 2480 } 2481 2482 SearchResult searchResult; 2483 boolean connectionValid = false; 2484 try 2485 { 2486 try 2487 { 2488 searchResult = connection.search(searchRequest); 2489 } 2490 catch (final LDAPSearchException lse) 2491 { 2492 searchResult = lse.getSearchResult(); 2493 } 2494 2495 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2496 { 2497 connectionValid = true; 2498 } 2499 else if (searchResult.getResultCode().isConnectionUsable()) 2500 { 2501 connectionValid = true; 2502 writeRejectedChange(rejectWriter, 2503 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2504 String.valueOf(filter)), 2505 modifyChangeRecord, searchResult); 2506 return searchResult.getResultCode(); 2507 } 2508 else if (retryFailedOperations.isPresent()) 2509 { 2510 try 2511 { 2512 connection = connectionPool.replaceDefunctConnection(connection); 2513 } 2514 catch (final LDAPException le) 2515 { 2516 Debug.debugException(le); 2517 writeRejectedChange(rejectWriter, 2518 ERR_LDAPMODIFY_SEARCH_FAILED_CANNOT_RECONNECT.get( 2519 modifyChangeRecord.getDN(), String.valueOf(filter)), 2520 modifyChangeRecord, searchResult); 2521 return searchResult.getResultCode(); 2522 } 2523 2524 try 2525 { 2526 searchResult = connection.search(searchRequest); 2527 } 2528 catch (final LDAPSearchException lse) 2529 { 2530 Debug.debugException(lse); 2531 searchResult = lse.getSearchResult(); 2532 } 2533 2534 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2535 { 2536 connectionValid = true; 2537 } 2538 else 2539 { 2540 connectionValid = searchResult.getResultCode().isConnectionUsable(); 2541 writeRejectedChange(rejectWriter, 2542 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2543 String.valueOf(filter)), 2544 modifyChangeRecord, searchResult); 2545 return searchResult.getResultCode(); 2546 } 2547 } 2548 else 2549 { 2550 writeRejectedChange(rejectWriter, 2551 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2552 String.valueOf(filter)), 2553 modifyChangeRecord, searchResult); 2554 return searchResult.getResultCode(); 2555 } 2556 } 2557 finally 2558 { 2559 if (connectionValid) 2560 { 2561 connectionPool.releaseConnection(connection); 2562 } 2563 else 2564 { 2565 connectionPool.releaseDefunctConnection(connection); 2566 } 2567 } 2568 2569 2570 // If we've gotten here, then the search was successful. Check to see if 2571 // any of the modifications failed, and if so then update the result code 2572 // accordingly. 2573 if ((resultCode == ResultCode.SUCCESS) && 2574 (listener.getResultCode() != ResultCode.SUCCESS)) 2575 { 2576 resultCode = listener.getResultCode(); 2577 } 2578 2579 2580 // If the search used the simple paged results control then we may need to 2581 // repeat the search to get the next page. 2582 entriesProcessed += searchResult.getEntryCount(); 2583 if (searchPageSize.isPresent()) 2584 { 2585 final SimplePagedResultsControl responseControl; 2586 try 2587 { 2588 responseControl = SimplePagedResultsControl.get(searchResult); 2589 } 2590 catch (final LDAPException le) 2591 { 2592 Debug.debugException(le); 2593 writeRejectedChange(rejectWriter, 2594 ERR_LDAPMODIFY_CANNOT_DECODE_PAGED_RESULTS_CONTROL.get( 2595 modifyChangeRecord.getDN(), String.valueOf(filter)), 2596 modifyChangeRecord, le.toLDAPResult()); 2597 return le.getResultCode(); 2598 } 2599 2600 if (responseControl == null) 2601 { 2602 writeRejectedChange(rejectWriter, 2603 ERR_LDAPMODIFY_MISSING_PAGED_RESULTS_RESPONSE.get( 2604 modifyChangeRecord.getDN(), String.valueOf(filter)), 2605 modifyChangeRecord); 2606 return ResultCode.CONTROL_NOT_FOUND; 2607 } 2608 else 2609 { 2610 pagedResultsCookie = responseControl.getCookie(); 2611 if (responseControl.moreResultsToReturn()) 2612 { 2613 if (verbose.isPresent()) 2614 { 2615 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED_MORE_PAGES.get( 2616 modifyChangeRecord.getDN(), String.valueOf(filter), 2617 entriesProcessed)); 2618 for (final String resultLine : 2619 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2620 { 2621 out(resultLine); 2622 } 2623 out(); 2624 } 2625 } 2626 else 2627 { 2628 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get( 2629 entriesProcessed, modifyChangeRecord.getDN(), 2630 String.valueOf(filter))); 2631 if (verbose.isPresent()) 2632 { 2633 for (final String resultLine : 2634 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2635 { 2636 out(resultLine); 2637 } 2638 } 2639 2640 out(); 2641 return resultCode; 2642 } 2643 } 2644 } 2645 else 2646 { 2647 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get( 2648 entriesProcessed, modifyChangeRecord.getDN(), 2649 String.valueOf(filter))); 2650 if (verbose.isPresent()) 2651 { 2652 for (final String resultLine : 2653 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2654 { 2655 out(resultLine); 2656 } 2657 } 2658 2659 out(); 2660 return resultCode; 2661 } 2662 } 2663 } 2664 2665 2666 2667 /** 2668 * Handles the processing for a change record when the tool should modify an 2669 * entry with a given DN instead of the DN contained in the change record. 2670 * 2671 * @param connectionPool The connection pool to use to communicate with 2672 * the directory server. 2673 * @param changeRecord The LDIF change record to be processed. 2674 * @param argIdentifierString The identifier string for the argument used to 2675 * specify the DN of the entry to modify. 2676 * @param dn The DN of the entry to modify. 2677 * @param modifyControls The set of controls to include in the modify 2678 * requests. 2679 * @param rateLimiter The fixed-rate barrier to use for rate 2680 * limiting. It may be {@code null} if no rate 2681 * limiting is required. 2682 * @param rejectWriter The reject writer to use to record information 2683 * about any failed operations. 2684 * 2685 * @return A result code obtained from processing. 2686 */ 2687 private ResultCode handleModifyWithDN( 2688 final LDAPConnectionPool connectionPool, 2689 final LDIFChangeRecord changeRecord, 2690 final String argIdentifierString, final DN dn, 2691 final List<Control> modifyControls, 2692 final FixedRateBarrier rateLimiter, 2693 final LDIFWriter rejectWriter) 2694 { 2695 // If the provided change record isn't a modify change record, then that's 2696 // an error. Reject it. 2697 if (! (changeRecord instanceof LDIFModifyChangeRecord)) 2698 { 2699 writeRejectedChange(rejectWriter, 2700 ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString), 2701 changeRecord); 2702 return ResultCode.PARAM_ERROR; 2703 } 2704 2705 2706 // Create a new modify change record with the provided DN instead of the 2707 // original DN. 2708 final LDIFModifyChangeRecord originalChangeRecord = 2709 (LDIFModifyChangeRecord) changeRecord; 2710 final LDIFModifyChangeRecord updatedChangeRecord = 2711 new LDIFModifyChangeRecord(dn.toString(), 2712 originalChangeRecord.getModifications(), 2713 originalChangeRecord.getControls()); 2714 2715 if (rateLimiter != null) 2716 { 2717 rateLimiter.await(); 2718 } 2719 2720 try 2721 { 2722 return doModify(updatedChangeRecord, modifyControls, connectionPool, null, 2723 rejectWriter); 2724 } 2725 catch (final LDAPException le) 2726 { 2727 Debug.debugException(le); 2728 return le.getResultCode(); 2729 } 2730 } 2731 2732 2733 2734 /** 2735 * Populates lists of request controls that should be included in requests 2736 * of various types. 2737 * 2738 * @param addControls The list of controls to include in add requests. 2739 * @param deleteControls The list of controls to include in delete 2740 * requests. 2741 * @param modifyControls The list of controls to include in modify 2742 * requests. 2743 * @param modifyDNControls The list of controls to include in modify DN 2744 * requests. 2745 * @param searchControls The list of controls to include in search 2746 * requests. 2747 * 2748 * @throws LDAPException If a problem is encountered while creating any of 2749 * the requested controls. 2750 */ 2751 private void createRequestControls(final List<Control> addControls, 2752 final List<Control> deleteControls, 2753 final List<Control> modifyControls, 2754 final List<Control> modifyDNControls, 2755 final List<Control> searchControls) 2756 throws LDAPException 2757 { 2758 if (addControl.isPresent()) 2759 { 2760 addControls.addAll(addControl.getValues()); 2761 } 2762 2763 if (deleteControl.isPresent()) 2764 { 2765 deleteControls.addAll(deleteControl.getValues()); 2766 } 2767 2768 if (modifyControl.isPresent()) 2769 { 2770 modifyControls.addAll(modifyControl.getValues()); 2771 } 2772 2773 if (modifyDNControl.isPresent()) 2774 { 2775 modifyDNControls.addAll(modifyDNControl.getValues()); 2776 } 2777 2778 if (operationControl.isPresent()) 2779 { 2780 addControls.addAll(operationControl.getValues()); 2781 deleteControls.addAll(operationControl.getValues()); 2782 modifyControls.addAll(operationControl.getValues()); 2783 modifyDNControls.addAll(operationControl.getValues()); 2784 } 2785 2786 addControls.addAll(routeToBackendSetRequestControls); 2787 deleteControls.addAll(routeToBackendSetRequestControls); 2788 modifyControls.addAll(routeToBackendSetRequestControls); 2789 modifyDNControls.addAll(routeToBackendSetRequestControls); 2790 2791 if (noOperation.isPresent()) 2792 { 2793 final NoOpRequestControl c = new NoOpRequestControl(); 2794 addControls.add(c); 2795 deleteControls.add(c); 2796 modifyControls.add(c); 2797 modifyDNControls.add(c); 2798 } 2799 2800 if (generatePassword.isPresent()) 2801 { 2802 addControls.add(new GeneratePasswordRequestControl()); 2803 } 2804 2805 if (getBackendSetID.isPresent()) 2806 { 2807 final GetBackendSetIDRequestControl c = 2808 new GetBackendSetIDRequestControl(false); 2809 addControls.add(c); 2810 deleteControls.add(c); 2811 modifyControls.add(c); 2812 modifyDNControls.add(c); 2813 } 2814 2815 if (getServerID.isPresent()) 2816 { 2817 final GetServerIDRequestControl c = 2818 new GetServerIDRequestControl(false); 2819 addControls.add(c); 2820 deleteControls.add(c); 2821 modifyControls.add(c); 2822 modifyDNControls.add(c); 2823 } 2824 2825 if (ignoreNoUserModification.isPresent()) 2826 { 2827 addControls.add(new IgnoreNoUserModificationRequestControl(false)); 2828 modifyControls.add(new IgnoreNoUserModificationRequestControl(false)); 2829 } 2830 2831 if (nameWithEntryUUID.isPresent()) 2832 { 2833 addControls.add(new NameWithEntryUUIDRequestControl(true)); 2834 } 2835 2836 if (permissiveModify.isPresent()) 2837 { 2838 modifyControls.add(new PermissiveModifyRequestControl(false)); 2839 } 2840 2841 if (routeToServer.isPresent()) 2842 { 2843 final RouteToServerRequestControl c = 2844 new RouteToServerRequestControl(false, 2845 routeToServer.getValue(), false, false, false); 2846 addControls.add(c); 2847 deleteControls.add(c); 2848 modifyControls.add(c); 2849 modifyDNControls.add(c); 2850 } 2851 2852 if (suppressReferentialIntegrityUpdates.isPresent()) 2853 { 2854 final SuppressReferentialIntegrityUpdatesRequestControl c = 2855 new SuppressReferentialIntegrityUpdatesRequestControl(true); 2856 deleteControls.add(c); 2857 modifyDNControls.add(c); 2858 } 2859 2860 if (suppressOperationalAttributeUpdates.isPresent()) 2861 { 2862 final EnumSet<SuppressType> suppressTypes = 2863 EnumSet.noneOf(SuppressType.class); 2864 for (final String s : suppressOperationalAttributeUpdates.getValues()) 2865 { 2866 if (s.equalsIgnoreCase("last-access-time")) 2867 { 2868 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 2869 } 2870 else if (s.equalsIgnoreCase("last-login-time")) 2871 { 2872 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 2873 } 2874 else if (s.equalsIgnoreCase("last-login-ip")) 2875 { 2876 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 2877 } 2878 else if (s.equalsIgnoreCase("lastmod")) 2879 { 2880 suppressTypes.add(SuppressType.LASTMOD); 2881 } 2882 } 2883 2884 final SuppressOperationalAttributeUpdateRequestControl c = 2885 new SuppressOperationalAttributeUpdateRequestControl(suppressTypes); 2886 addControls.add(c); 2887 deleteControls.add(c); 2888 modifyControls.add(c); 2889 modifyDNControls.add(c); 2890 } 2891 2892 if (usePasswordPolicyControl.isPresent()) 2893 { 2894 final PasswordPolicyRequestControl c = new PasswordPolicyRequestControl(); 2895 addControls.add(c); 2896 modifyControls.add(c); 2897 } 2898 2899 if (assuredReplication.isPresent()) 2900 { 2901 AssuredReplicationLocalLevel localLevel = null; 2902 if (assuredReplicationLocalLevel.isPresent()) 2903 { 2904 final String level = assuredReplicationLocalLevel.getValue(); 2905 if (level.equalsIgnoreCase("none")) 2906 { 2907 localLevel = AssuredReplicationLocalLevel.NONE; 2908 } 2909 else if (level.equalsIgnoreCase("received-any-server")) 2910 { 2911 localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER; 2912 } 2913 else if (level.equalsIgnoreCase("processed-all-servers")) 2914 { 2915 localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS; 2916 } 2917 } 2918 2919 AssuredReplicationRemoteLevel remoteLevel = null; 2920 if (assuredReplicationRemoteLevel.isPresent()) 2921 { 2922 final String level = assuredReplicationRemoteLevel.getValue(); 2923 if (level.equalsIgnoreCase("none")) 2924 { 2925 remoteLevel = AssuredReplicationRemoteLevel.NONE; 2926 } 2927 else if (level.equalsIgnoreCase("received-any-remote-location")) 2928 { 2929 remoteLevel = 2930 AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION; 2931 } 2932 else if (level.equalsIgnoreCase("received-all-remote-locations")) 2933 { 2934 remoteLevel = 2935 AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS; 2936 } 2937 else if (level.equalsIgnoreCase("processed-all-remote-servers")) 2938 { 2939 remoteLevel = 2940 AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS; 2941 } 2942 } 2943 2944 Long timeoutMillis = null; 2945 if (assuredReplicationTimeout.isPresent()) 2946 { 2947 timeoutMillis = 2948 assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS); 2949 } 2950 2951 final AssuredReplicationRequestControl c = 2952 new AssuredReplicationRequestControl(true, localLevel, localLevel, 2953 remoteLevel, remoteLevel, timeoutMillis, false); 2954 addControls.add(c); 2955 deleteControls.add(c); 2956 modifyControls.add(c); 2957 modifyDNControls.add(c); 2958 } 2959 2960 if (hardDelete.isPresent() && (! clientSideSubtreeDelete.isPresent())) 2961 { 2962 deleteControls.add(new HardDeleteRequestControl(true)); 2963 } 2964 2965 if (replicationRepair.isPresent()) 2966 { 2967 final ReplicationRepairRequestControl c = 2968 new ReplicationRepairRequestControl(); 2969 addControls.add(c); 2970 deleteControls.add(c); 2971 modifyControls.add(c); 2972 modifyDNControls.add(c); 2973 } 2974 2975 if (softDelete.isPresent()) 2976 { 2977 deleteControls.add(new SoftDeleteRequestControl(true, true)); 2978 } 2979 2980 if (serverSideSubtreeDelete.isPresent()) 2981 { 2982 deleteControls.add(new SubtreeDeleteRequestControl()); 2983 } 2984 2985 if (assertionFilter.isPresent()) 2986 { 2987 final AssertionRequestControl c = new AssertionRequestControl( 2988 assertionFilter.getValue(), true); 2989 addControls.add(c); 2990 deleteControls.add(c); 2991 modifyControls.add(c); 2992 modifyDNControls.add(c); 2993 } 2994 2995 if (operationPurpose.isPresent()) 2996 { 2997 final OperationPurposeRequestControl c = 2998 new OperationPurposeRequestControl(false, "ldapmodify", 2999 Version.NUMERIC_VERSION_STRING, 3000 LDAPModify.class.getName() + ".createRequestControls", 3001 operationPurpose.getValue()); 3002 addControls.add(c); 3003 deleteControls.add(c); 3004 modifyControls.add(c); 3005 modifyDNControls.add(c); 3006 } 3007 3008 if (manageDsaIT.isPresent()) 3009 { 3010 final ManageDsaITRequestControl c = new ManageDsaITRequestControl(true); 3011 addControls.add(c); 3012 if (! clientSideSubtreeDelete.isPresent()) 3013 { 3014 deleteControls.add(c); 3015 } 3016 modifyControls.add(c); 3017 modifyDNControls.add(c); 3018 } 3019 3020 if (passwordUpdateBehavior.isPresent()) 3021 { 3022 final PasswordUpdateBehaviorRequestControl c = 3023 createPasswordUpdateBehaviorRequestControl( 3024 passwordUpdateBehavior.getIdentifierString(), 3025 passwordUpdateBehavior.getValues()); 3026 addControls.add(c); 3027 modifyControls.add(c); 3028 } 3029 3030 if (preReadAttribute.isPresent()) 3031 { 3032 final ArrayList<String> attrList = new ArrayList<>(10); 3033 for (final String value : preReadAttribute.getValues()) 3034 { 3035 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 3036 while (tokenizer.hasMoreTokens()) 3037 { 3038 attrList.add(tokenizer.nextToken()); 3039 } 3040 } 3041 3042 final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS); 3043 final PreReadRequestControl c = new PreReadRequestControl(attrArray); 3044 deleteControls.add(c); 3045 modifyControls.add(c); 3046 modifyDNControls.add(c); 3047 } 3048 3049 if (postReadAttribute.isPresent()) 3050 { 3051 final ArrayList<String> attrList = new ArrayList<>(10); 3052 for (final String value : postReadAttribute.getValues()) 3053 { 3054 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 3055 while (tokenizer.hasMoreTokens()) 3056 { 3057 attrList.add(tokenizer.nextToken()); 3058 } 3059 } 3060 3061 final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS); 3062 final PostReadRequestControl c = new PostReadRequestControl(attrArray); 3063 addControls.add(c); 3064 modifyControls.add(c); 3065 modifyDNControls.add(c); 3066 } 3067 3068 if (proxyAs.isPresent() && (! useTransaction.isPresent()) && 3069 (! multiUpdateErrorBehavior.isPresent())) 3070 { 3071 final ProxiedAuthorizationV2RequestControl c = 3072 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()); 3073 addControls.add(c); 3074 deleteControls.add(c); 3075 modifyControls.add(c); 3076 modifyDNControls.add(c); 3077 searchControls.add(c); 3078 } 3079 3080 if (proxyV1As.isPresent() && (! useTransaction.isPresent()) && 3081 (! multiUpdateErrorBehavior.isPresent())) 3082 { 3083 final ProxiedAuthorizationV1RequestControl c = 3084 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()); 3085 addControls.add(c); 3086 deleteControls.add(c); 3087 modifyControls.add(c); 3088 modifyDNControls.add(c); 3089 searchControls.add(c); 3090 } 3091 3092 if (uniquenessAttribute.isPresent() || uniquenessFilter.isPresent()) 3093 { 3094 final UniquenessRequestControlProperties uniquenessProperties; 3095 if (uniquenessAttribute.isPresent()) 3096 { 3097 uniquenessProperties = new UniquenessRequestControlProperties( 3098 uniquenessAttribute.getValues()); 3099 if (uniquenessFilter.isPresent()) 3100 { 3101 uniquenessProperties.setFilter(uniquenessFilter.getValue()); 3102 } 3103 } 3104 else 3105 { 3106 uniquenessProperties = new UniquenessRequestControlProperties( 3107 uniquenessFilter.getValue()); 3108 } 3109 3110 if (uniquenessBaseDN.isPresent()) 3111 { 3112 uniquenessProperties.setBaseDN(uniquenessBaseDN.getStringValue()); 3113 } 3114 3115 if (uniquenessMultipleAttributeBehavior.isPresent()) 3116 { 3117 final String value = 3118 uniquenessMultipleAttributeBehavior.getValue().toLowerCase(); 3119 switch (value) 3120 { 3121 case "unique-within-each-attribute": 3122 uniquenessProperties.setMultipleAttributeBehavior( 3123 UniquenessMultipleAttributeBehavior. 3124 UNIQUE_WITHIN_EACH_ATTRIBUTE); 3125 break; 3126 case "unique-across-all-attributes-including-in-same-entry": 3127 uniquenessProperties.setMultipleAttributeBehavior( 3128 UniquenessMultipleAttributeBehavior. 3129 UNIQUE_ACROSS_ALL_ATTRIBUTES_INCLUDING_IN_SAME_ENTRY); 3130 break; 3131 case "unique-across-all-attributes-except-in-same-entry": 3132 uniquenessProperties.setMultipleAttributeBehavior( 3133 UniquenessMultipleAttributeBehavior. 3134 UNIQUE_ACROSS_ALL_ATTRIBUTES_EXCEPT_IN_SAME_ENTRY); 3135 break; 3136 case "unique-in-combination": 3137 uniquenessProperties.setMultipleAttributeBehavior( 3138 UniquenessMultipleAttributeBehavior.UNIQUE_IN_COMBINATION); 3139 break; 3140 } 3141 } 3142 3143 if (uniquenessPreCommitValidationLevel.isPresent()) 3144 { 3145 final String value = 3146 uniquenessPreCommitValidationLevel.getValue().toLowerCase(); 3147 switch (value) 3148 { 3149 case "none": 3150 uniquenessProperties.setPreCommitValidationLevel( 3151 UniquenessValidationLevel.NONE); 3152 break; 3153 case "all-subtree-views": 3154 uniquenessProperties.setPreCommitValidationLevel( 3155 UniquenessValidationLevel.ALL_SUBTREE_VIEWS); 3156 break; 3157 case "all-backend-sets": 3158 uniquenessProperties.setPreCommitValidationLevel( 3159 UniquenessValidationLevel.ALL_BACKEND_SETS); 3160 break; 3161 case "all-available-backend-servers": 3162 uniquenessProperties.setPreCommitValidationLevel( 3163 UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS); 3164 break; 3165 } 3166 } 3167 3168 if (uniquenessPostCommitValidationLevel.isPresent()) 3169 { 3170 final String value = 3171 uniquenessPostCommitValidationLevel.getValue().toLowerCase(); 3172 switch (value) 3173 { 3174 case "none": 3175 uniquenessProperties.setPostCommitValidationLevel( 3176 UniquenessValidationLevel.NONE); 3177 break; 3178 case "all-subtree-views": 3179 uniquenessProperties.setPostCommitValidationLevel( 3180 UniquenessValidationLevel.ALL_SUBTREE_VIEWS); 3181 break; 3182 case "all-backend-sets": 3183 uniquenessProperties.setPostCommitValidationLevel( 3184 UniquenessValidationLevel.ALL_BACKEND_SETS); 3185 break; 3186 case "all-available-backend-servers": 3187 uniquenessProperties.setPostCommitValidationLevel( 3188 UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS); 3189 break; 3190 } 3191 } 3192 3193 final UniquenessRequestControl c = 3194 new UniquenessRequestControl(true, null, uniquenessProperties); 3195 addControls.add(c); 3196 modifyControls.add(c); 3197 modifyDNControls.add(c); 3198 } 3199 } 3200 3201 3202 3203 /** 3204 * Creates the password update behavior request control that should be 3205 * included in add and modify requests. 3206 * 3207 * @param argIdentifier The identifier string for the argument used to 3208 * configure the password update behavior request 3209 * control. 3210 * @param argValues The set of values for the password update behavior 3211 * request control. 3212 * 3213 * @return The password update behavior request control that was created. 3214 * 3215 * @throws LDAPException If a problem is encountered while creating the 3216 * control. 3217 */ 3218 static PasswordUpdateBehaviorRequestControl 3219 createPasswordUpdateBehaviorRequestControl( 3220 final String argIdentifier, final List<String> argValues) 3221 throws LDAPException 3222 { 3223 final PasswordUpdateBehaviorRequestControlProperties properties = 3224 new PasswordUpdateBehaviorRequestControlProperties(); 3225 3226 for (final String argValue : argValues) 3227 { 3228 int delimiterPos = argValue.indexOf('='); 3229 if (delimiterPos < 0) 3230 { 3231 delimiterPos = argValue.indexOf(':'); 3232 } 3233 3234 if ((delimiterPos <= 0) || (delimiterPos >= (argValue.length() - 1))) 3235 { 3236 throw new LDAPException(ResultCode.PARAM_ERROR, 3237 ERR_LDAPMODIFY_MALFORMED_PW_UPDATE_BEHAVIOR.get(argValue, 3238 argIdentifier)); 3239 } 3240 3241 final String name = argValue.substring(0, delimiterPos).trim(); 3242 final String value = argValue.substring(delimiterPos+1).trim(); 3243 if (name.equalsIgnoreCase("is-self-change") || 3244 name.equalsIgnoreCase("self-change") || 3245 name.equalsIgnoreCase("isSelfChange") || 3246 name.equalsIgnoreCase("selfChange")) 3247 { 3248 properties.setIsSelfChange(parseBooleanValue(name, value)); 3249 } 3250 else if (name.equalsIgnoreCase("allow-pre-encoded-password") || 3251 name.equalsIgnoreCase("allow-pre-encoded-passwords") || 3252 name.equalsIgnoreCase("allow-pre-encoded") || 3253 name.equalsIgnoreCase("allowPreEncodedPassword") || 3254 name.equalsIgnoreCase("allowPreEncodedPasswords") || 3255 name.equalsIgnoreCase("allowPreEncoded")) 3256 { 3257 properties.setAllowPreEncodedPassword(parseBooleanValue(name, value)); 3258 } 3259 else if (name.equalsIgnoreCase("skip-password-validation") || 3260 name.equalsIgnoreCase("skip-password-validators") || 3261 name.equalsIgnoreCase("skip-validation") || 3262 name.equalsIgnoreCase("skip-validators") || 3263 name.equalsIgnoreCase("skipPasswordValidation") || 3264 name.equalsIgnoreCase("skipPasswordValidators") || 3265 name.equalsIgnoreCase("skipValidation") || 3266 name.equalsIgnoreCase("skipValidators")) 3267 { 3268 properties.setSkipPasswordValidation(parseBooleanValue(name, value)); 3269 } 3270 else if (name.equalsIgnoreCase("ignore-password-history") || 3271 name.equalsIgnoreCase("skip-password-history") || 3272 name.equalsIgnoreCase("ignore-history") || 3273 name.equalsIgnoreCase("skip-history") || 3274 name.equalsIgnoreCase("ignorePasswordHistory") || 3275 name.equalsIgnoreCase("skipPasswordHistory") || 3276 name.equalsIgnoreCase("ignoreHistory") || 3277 name.equalsIgnoreCase("skipHistory")) 3278 { 3279 properties.setIgnorePasswordHistory(parseBooleanValue(name, value)); 3280 } 3281 else if (name.equalsIgnoreCase("ignore-minimum-password-age") || 3282 name.equalsIgnoreCase("ignore-min-password-age") || 3283 name.equalsIgnoreCase("ignore-password-age") || 3284 name.equalsIgnoreCase("skip-minimum-password-age") || 3285 name.equalsIgnoreCase("skip-min-password-age") || 3286 name.equalsIgnoreCase("skip-password-age") || 3287 name.equalsIgnoreCase("ignoreMinimumPasswordAge") || 3288 name.equalsIgnoreCase("ignoreMinPasswordAge") || 3289 name.equalsIgnoreCase("ignorePasswordAge") || 3290 name.equalsIgnoreCase("skipMinimumPasswordAge") || 3291 name.equalsIgnoreCase("skipMinPasswordAge") || 3292 name.equalsIgnoreCase("skipPasswordAge")) 3293 { 3294 properties.setIgnoreMinimumPasswordAge(parseBooleanValue(name, value)); 3295 } 3296 else if (name.equalsIgnoreCase("password-storage-scheme") || 3297 name.equalsIgnoreCase("password-scheme") || 3298 name.equalsIgnoreCase("storage-scheme") || 3299 name.equalsIgnoreCase("scheme") || 3300 name.equalsIgnoreCase("passwordStorageScheme") || 3301 name.equalsIgnoreCase("passwordScheme") || 3302 name.equalsIgnoreCase("storageScheme")) 3303 { 3304 properties.setPasswordStorageScheme(value); 3305 } 3306 else if (name.equalsIgnoreCase("must-change-password") || 3307 name.equalsIgnoreCase("mustChangePassword")) 3308 { 3309 properties.setMustChangePassword(parseBooleanValue(name, value)); 3310 } 3311 } 3312 3313 return new PasswordUpdateBehaviorRequestControl(properties, true); 3314 } 3315 3316 3317 3318 /** 3319 * Parses the provided value as the Boolean value for a password update 3320 * behavior property. 3321 * 3322 * @param name The name of the password update behavior property being 3323 * parsed. 3324 * @param value The value to be parsed. 3325 * 3326 * @return The Boolean value that was parsed. 3327 * 3328 * @throws LDAPException If the provided value cannot be parsed as a 3329 * Boolean value. 3330 */ 3331 private static boolean parseBooleanValue(final String name, 3332 final String value) 3333 throws LDAPException 3334 { 3335 if (value.equalsIgnoreCase("true") || 3336 value.equalsIgnoreCase("t") || 3337 value.equalsIgnoreCase("yes") || 3338 value.equalsIgnoreCase("y") || 3339 value.equalsIgnoreCase("1")) 3340 { 3341 return true; 3342 } 3343 else if (value.equalsIgnoreCase("false") || 3344 value.equalsIgnoreCase("f") || 3345 value.equalsIgnoreCase("no") || 3346 value.equalsIgnoreCase("n") || 3347 value.equalsIgnoreCase("0")) 3348 { 3349 return false; 3350 } 3351 else 3352 { 3353 throw new LDAPException(ResultCode.PARAM_ERROR, 3354 ERR_LDAPMODIFY_INVALID_PW_UPDATE_BOOLEAN_VALUE.get(value, name)); 3355 } 3356 } 3357 3358 3359 3360 /** 3361 * Performs the appropriate processing for an LDIF add change record. 3362 * 3363 * @param changeRecord The LDIF add change record to process. 3364 * @param controls The set of controls to include in the request. 3365 * @param pool The connection pool to use to communicate with 3366 * the directory server. 3367 * @param multiUpdateRequests The list to which the request should be added 3368 * if it is to be processed as part of a 3369 * multi-update operation. It may be 3370 * {@code null} if the operation should not be 3371 * processed via the multi-update operation. 3372 * @param rejectWriter The LDIF writer to use for recording 3373 * information about rejected changes. It may be 3374 * {@code null} if no reject writer is 3375 * configured. 3376 * 3377 * @return The result code obtained from processing. 3378 * 3379 * @throws LDAPException If the operation did not complete successfully 3380 * and processing should not continue. 3381 */ 3382 private ResultCode doAdd(final LDIFAddChangeRecord changeRecord, 3383 final List<Control> controls, 3384 final LDAPConnectionPool pool, 3385 final List<LDAPRequest> multiUpdateRequests, 3386 final LDIFWriter rejectWriter) 3387 throws LDAPException 3388 { 3389 // Create the add request to process. 3390 final AddRequest addRequest = changeRecord.toAddRequest(true); 3391 for (final Control c : controls) 3392 { 3393 addRequest.addControl(c); 3394 } 3395 3396 3397 // If we should provide support for undelete operations and the entry 3398 // includes the ds-undelete-from-dn attribute, then add the undelete request 3399 // control. 3400 if (allowUndelete.isPresent() && 3401 addRequest.hasAttribute(ATTR_UNDELETE_FROM_DN)) 3402 { 3403 addRequest.addControl(new UndeleteRequestControl()); 3404 } 3405 3406 3407 // If the entry to add includes a password, then add a password validation 3408 // details request control if appropriate. 3409 if (passwordValidationDetails.isPresent()) 3410 { 3411 final Entry entryToAdd = addRequest.toEntry(); 3412 if ((! entryToAdd.getAttributesWithOptions(ATTR_USER_PASSWORD, 3413 null).isEmpty()) || 3414 (! entryToAdd.getAttributesWithOptions(ATTR_AUTH_PASSWORD, 3415 null).isEmpty())) 3416 { 3417 addRequest.addControl(new PasswordValidationDetailsRequestControl()); 3418 } 3419 } 3420 3421 3422 // If the operation should be processed in a multi-update operation, then 3423 // just add the request to the list and return without doing anything else. 3424 if (multiUpdateErrorBehavior.isPresent()) 3425 { 3426 multiUpdateRequests.add(addRequest); 3427 commentToOut(INFO_LDAPMODIFY_ADD_ADDED_TO_MULTI_UPDATE.get( 3428 addRequest.getDN())); 3429 return ResultCode.SUCCESS; 3430 } 3431 3432 3433 // If the --dryRun argument was provided, then we'll stop here. 3434 if (dryRun.isPresent()) 3435 { 3436 commentToOut(INFO_LDAPMODIFY_DRY_RUN_ADD.get(addRequest.getDN(), 3437 dryRun.getIdentifierString())); 3438 return ResultCode.SUCCESS; 3439 } 3440 3441 3442 // Process the add operation and get the result. 3443 commentToOut(INFO_LDAPMODIFY_ADDING_ENTRY.get(addRequest.getDN())); 3444 if (verbose.isPresent()) 3445 { 3446 for (final String ldifLine : 3447 addRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 3448 { 3449 out(ldifLine); 3450 } 3451 out(); 3452 } 3453 3454 LDAPResult addResult; 3455 try 3456 { 3457 addResult = pool.add(addRequest); 3458 } 3459 catch (final LDAPException le) 3460 { 3461 Debug.debugException(le); 3462 addResult = le.toLDAPResult(); 3463 } 3464 3465 3466 // Display information about the result. 3467 displayResult(addResult, useTransaction.isPresent()); 3468 3469 3470 // See if the add operation succeeded or failed. If it failed, and we 3471 // should end all processing, then throw an exception. 3472 switch (addResult.getResultCode().intValue()) 3473 { 3474 case ResultCode.SUCCESS_INT_VALUE: 3475 case ResultCode.NO_OPERATION_INT_VALUE: 3476 break; 3477 3478 case ResultCode.ASSERTION_FAILED_INT_VALUE: 3479 writeRejectedChange(rejectWriter, 3480 INFO_LDAPMODIFY_ASSERTION_FAILED.get(addRequest.getDN(), 3481 String.valueOf(assertionFilter.getValue())), 3482 addRequest.toLDIFChangeRecord(), addResult); 3483 throw new LDAPException(addResult); 3484 3485 default: 3486 writeRejectedChange(rejectWriter, null, addRequest.toLDIFChangeRecord(), 3487 addResult); 3488 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 3489 { 3490 throw new LDAPException(addResult); 3491 } 3492 break; 3493 } 3494 3495 return addResult.getResultCode(); 3496 } 3497 3498 3499 3500 /** 3501 * Performs the appropriate processing for an LDIF delete change record. 3502 * 3503 * @param changeRecord The LDIF delete change record to process. 3504 * @param controls The set of controls to include in the request. 3505 * @param pool The connection pool to use to communicate with 3506 * the directory server. 3507 * @param multiUpdateRequests The list to which the request should be added 3508 * if it is to be processed as part of a 3509 * multi-update operation. It may be 3510 * {@code null} if the operation should not be 3511 * processed via the multi-update operation. 3512 * @param rejectWriter The LDIF writer to use for recording 3513 * information about rejected changes. It may be 3514 * {@code null} if no reject writer is 3515 * configured. 3516 * 3517 * @return The result code obtained from processing. 3518 * 3519 * @throws LDAPException If the operation did not complete successfully 3520 * and processing should not continue. 3521 */ 3522 private ResultCode doDelete(final LDIFDeleteChangeRecord changeRecord, 3523 final List<Control> controls, 3524 final LDAPConnectionPool pool, 3525 final List<LDAPRequest> multiUpdateRequests, 3526 final LDIFWriter rejectWriter) 3527 throws LDAPException 3528 { 3529 // If we should perform a client-side subtree delete, then do that 3530 // differently. 3531 if (clientSideSubtreeDelete.isPresent()) 3532 { 3533 return doClientSideSubtreeDelete(changeRecord, controls, pool, 3534 rejectWriter); 3535 } 3536 3537 3538 // Create the delete request to process. 3539 final DeleteRequest deleteRequest = changeRecord.toDeleteRequest(true); 3540 for (final Control c : controls) 3541 { 3542 deleteRequest.addControl(c); 3543 } 3544 3545 3546 // If the operation should be processed in a multi-update operation, then 3547 // just add the request to the list and return without doing anything else. 3548 if (multiUpdateErrorBehavior.isPresent()) 3549 { 3550 multiUpdateRequests.add(deleteRequest); 3551 commentToOut(INFO_LDAPMODIFY_DELETE_ADDED_TO_MULTI_UPDATE.get( 3552 deleteRequest.getDN())); 3553 return ResultCode.SUCCESS; 3554 } 3555 3556 3557 // If the --dryRun argument was provided, then we'll stop here. 3558 if (dryRun.isPresent()) 3559 { 3560 commentToOut(INFO_LDAPMODIFY_DRY_RUN_DELETE.get(deleteRequest.getDN(), 3561 dryRun.getIdentifierString())); 3562 return ResultCode.SUCCESS; 3563 } 3564 3565 3566 // Process the delete operation and get the result. 3567 commentToOut(INFO_LDAPMODIFY_DELETING_ENTRY.get(deleteRequest.getDN())); 3568 if (verbose.isPresent()) 3569 { 3570 for (final String ldifLine : 3571 deleteRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 3572 { 3573 out(ldifLine); 3574 } 3575 out(); 3576 } 3577 3578 3579 LDAPResult deleteResult; 3580 try 3581 { 3582 deleteResult = pool.delete(deleteRequest); 3583 } 3584 catch (final LDAPException le) 3585 { 3586 Debug.debugException(le); 3587 deleteResult = le.toLDAPResult(); 3588 } 3589 3590 3591 // Display information about the result. 3592 displayResult(deleteResult, useTransaction.isPresent()); 3593 3594 3595 // See if the delete operation succeeded or failed. If it failed, and we 3596 // should end all processing, then throw an exception. 3597 switch (deleteResult.getResultCode().intValue()) 3598 { 3599 case ResultCode.SUCCESS_INT_VALUE: 3600 case ResultCode.NO_OPERATION_INT_VALUE: 3601 break; 3602 3603 case ResultCode.ASSERTION_FAILED_INT_VALUE: 3604 writeRejectedChange(rejectWriter, 3605 INFO_LDAPMODIFY_ASSERTION_FAILED.get(deleteRequest.getDN(), 3606 String.valueOf(assertionFilter.getValue())), 3607 deleteRequest.toLDIFChangeRecord(), deleteResult); 3608 throw new LDAPException(deleteResult); 3609 3610 default: 3611 writeRejectedChange(rejectWriter, null, 3612 deleteRequest.toLDIFChangeRecord(), deleteResult); 3613 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 3614 { 3615 throw new LDAPException(deleteResult); 3616 } 3617 break; 3618 } 3619 3620 return deleteResult.getResultCode(); 3621 } 3622 3623 3624 3625 /** 3626 * Performs the appropriate processing for an LDIF delete change record. 3627 * 3628 * @param changeRecord The LDIF delete change record to process. 3629 * @param controls The set of controls to include in the request. 3630 * @param pool The connection pool to use to communicate with the 3631 * directory server. 3632 * @param rejectWriter The LDIF writer to use for recording information 3633 * about rejected changes. It may be {@code null} if no 3634 * reject writer is configured. 3635 * 3636 * @return The result code obtained from processing. 3637 * 3638 * @throws LDAPException If the operation did not complete successfully 3639 * and processing should not continue. 3640 */ 3641 private ResultCode doClientSideSubtreeDelete( 3642 final LDIFChangeRecord changeRecord, 3643 final List<Control> controls, 3644 final LDAPConnectionPool pool, 3645 final LDIFWriter rejectWriter) 3646 throws LDAPException 3647 { 3648 // Create the subtree deleter with the provided set of controls. Make sure 3649 // to include any controls in the delete change record itself. 3650 final List<Control> additionalControls; 3651 if (changeRecord.getControls().isEmpty()) 3652 { 3653 additionalControls = controls; 3654 } 3655 else 3656 { 3657 additionalControls = new ArrayList<>(controls.size() + 3658 changeRecord.getControls().size()); 3659 additionalControls.addAll(changeRecord.getControls()); 3660 additionalControls.addAll(controls); 3661 } 3662 3663 final SubtreeDeleter subtreeDeleter = new SubtreeDeleter(); 3664 subtreeDeleter.setAdditionalDeleteControls(additionalControls); 3665 3666 3667 // Perform the subtree delete. 3668 commentToOut(INFO_LDAPMODIFY_CLIENT_SIDE_DELETING_SUBTREE.get( 3669 changeRecord.getDN())); 3670 final SubtreeDeleterResult subtreeDeleterResult = 3671 subtreeDeleter.delete(pool, changeRecord.getDN()); 3672 3673 3674 // Evaluate the result of the subtree delete. 3675 final LDAPResult finalResult; 3676 if (subtreeDeleterResult.completelySuccessful()) 3677 { 3678 final long entriesDeleted = subtreeDeleterResult.getEntriesDeleted(); 3679 if (entriesDeleted == 0L) 3680 { 3681 // This means that the base entry did not exist. Even though the 3682 // subtree deleter returned a successful result, we'll use a final 3683 // result of "no such object". 3684 finalResult = new LDAPResult(-1, ResultCode.NO_SUCH_OBJECT, 3685 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_0_ENTRIES.get( 3686 changeRecord.getDN()), 3687 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3688 } 3689 else if (entriesDeleted == 1L) 3690 { 3691 // This means the base entry existed (and we deleted it successfully), 3692 // but did not have any subordinates. 3693 finalResult = new LDAPResult(-1, ResultCode.SUCCESS, 3694 INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_1_ENTRY.get( 3695 changeRecord.getDN()), 3696 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3697 } 3698 else 3699 { 3700 // This means that the base entry existed and had subordinates, and we 3701 // deleted all of them successfully. 3702 finalResult = new LDAPResult(-1, ResultCode.SUCCESS, 3703 INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_ENTRIES.get( 3704 subtreeDeleterResult.getEntriesDeleted(), 3705 changeRecord.getDN()), 3706 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3707 } 3708 } 3709 else 3710 { 3711 // If there was a search error, then display information about it. 3712 final SearchResult searchError = subtreeDeleterResult.getSearchError(); 3713 if (searchError != null) 3714 { 3715 commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SEARCH_ERROR.get()); 3716 displayResult(searchError, false); 3717 err("#"); 3718 } 3719 3720 final SortedMap<DN,LDAPResult> deleteErrors = 3721 subtreeDeleterResult.getDeleteErrorsDescendingMap(); 3722 for (final Map.Entry<DN,LDAPResult> deleteError : deleteErrors.entrySet()) 3723 { 3724 commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_ERROR.get( 3725 String.valueOf(deleteError.getKey()))); 3726 displayResult(deleteError.getValue(), false); 3727 err("#"); 3728 } 3729 3730 ResultCode resultCode = ResultCode.OTHER; 3731 final StringBuilder buffer = new StringBuilder(); 3732 buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_ERR_BASE.get()); 3733 if (searchError != null) 3734 { 3735 resultCode = searchError.getResultCode(); 3736 buffer.append(" "); 3737 buffer.append( 3738 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_SEARCH_ERR.get()); 3739 } 3740 3741 if (! deleteErrors.isEmpty()) 3742 { 3743 resultCode = deleteErrors.values().iterator().next().getResultCode(); 3744 buffer.append(" "); 3745 final int numDeleteErrors = deleteErrors.size(); 3746 if (numDeleteErrors == 1) 3747 { 3748 buffer.append( 3749 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT_1.get()); 3750 } 3751 else 3752 { 3753 buffer.append( 3754 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT.get( 3755 numDeleteErrors)); 3756 } 3757 } 3758 3759 buffer.append(" "); 3760 final long deletedCount = subtreeDeleterResult.getEntriesDeleted(); 3761 if (deletedCount == 1L) 3762 { 3763 buffer.append( 3764 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT_1.get()); 3765 } 3766 else 3767 { 3768 buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT.get( 3769 deletedCount)); 3770 } 3771 3772 finalResult = new LDAPResult(-1, resultCode, buffer.toString(), null, 3773 StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3774 } 3775 3776 3777 // Display information about the final result. 3778 displayResult(finalResult, useTransaction.isPresent()); 3779 3780 3781 // See if the delete operation succeeded or failed. If it failed, and we 3782 // should end all processing, then throw an exception. 3783 switch (finalResult.getResultCode().intValue()) 3784 { 3785 case ResultCode.SUCCESS_INT_VALUE: 3786 case ResultCode.NO_OPERATION_INT_VALUE: 3787 break; 3788 3789 default: 3790 writeRejectedChange(rejectWriter, null, changeRecord, finalResult); 3791 if (! continueOnError.isPresent()) 3792 { 3793 throw new LDAPException(finalResult); 3794 } 3795 break; 3796 } 3797 3798 return finalResult.getResultCode(); 3799 } 3800 3801 3802 3803 /** 3804 * Performs the appropriate processing for an LDIF modify change record. 3805 * 3806 * @param changeRecord The LDIF modify change record to process. 3807 * @param controls The set of controls to include in the request. 3808 * @param pool The connection pool to use to communicate with 3809 * the directory server. 3810 * @param multiUpdateRequests The list to which the request should be added 3811 * if it is to be processed as part of a 3812 * multi-update operation. It may be 3813 * {@code null} if the operation should not be 3814 * processed via the multi-update operation. 3815 * @param rejectWriter The LDIF writer to use for recording 3816 * information about rejected changes. It may be 3817 * {@code null} if no reject writer is 3818 * configured. 3819 * 3820 * @return The result code obtained from processing. 3821 * 3822 * @throws LDAPException If the operation did not complete successfully 3823 * and processing should not continue. 3824 */ 3825 ResultCode doModify(final LDIFModifyChangeRecord changeRecord, 3826 final List<Control> controls, 3827 final LDAPConnectionPool pool, 3828 final List<LDAPRequest> multiUpdateRequests, 3829 final LDIFWriter rejectWriter) 3830 throws LDAPException 3831 { 3832 // Create the modify request to process. 3833 final ModifyRequest modifyRequest = changeRecord.toModifyRequest(true); 3834 for (final Control c : controls) 3835 { 3836 modifyRequest.addControl(c); 3837 } 3838 3839 3840 // If the modify request includes a password change, then add any controls 3841 // that are specific to that. 3842 if (retireCurrentPassword.isPresent() || purgeCurrentPassword.isPresent() || 3843 passwordValidationDetails.isPresent()) 3844 { 3845 for (final Modification m : modifyRequest.getModifications()) 3846 { 3847 final String baseName = m.getAttribute().getBaseName(); 3848 if (baseName.equalsIgnoreCase(ATTR_USER_PASSWORD) || 3849 baseName.equalsIgnoreCase(ATTR_AUTH_PASSWORD)) 3850 { 3851 if (retireCurrentPassword.isPresent()) 3852 { 3853 modifyRequest.addControl(new RetirePasswordRequestControl(false)); 3854 } 3855 else if (purgeCurrentPassword.isPresent()) 3856 { 3857 modifyRequest.addControl(new PurgePasswordRequestControl(false)); 3858 } 3859 3860 if (passwordValidationDetails.isPresent()) 3861 { 3862 modifyRequest.addControl( 3863 new PasswordValidationDetailsRequestControl()); 3864 } 3865 3866 break; 3867 } 3868 } 3869 } 3870 3871 3872 // If the operation should be processed in a multi-update operation, then 3873 // just add the request to the list and return without doing anything else. 3874 if (multiUpdateErrorBehavior.isPresent()) 3875 { 3876 multiUpdateRequests.add(modifyRequest); 3877 commentToOut(INFO_LDAPMODIFY_MODIFY_ADDED_TO_MULTI_UPDATE.get( 3878 modifyRequest.getDN())); 3879 return ResultCode.SUCCESS; 3880 } 3881 3882 3883 // If the --dryRun argument was provided, then we'll stop here. 3884 if (dryRun.isPresent()) 3885 { 3886 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MODIFY.get(modifyRequest.getDN(), 3887 dryRun.getIdentifierString())); 3888 return ResultCode.SUCCESS; 3889 } 3890 3891 3892 // Process the modify operation and get the result. 3893 commentToOut(INFO_LDAPMODIFY_MODIFYING_ENTRY.get(modifyRequest.getDN())); 3894 if (verbose.isPresent()) 3895 { 3896 for (final String ldifLine : 3897 modifyRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 3898 { 3899 out(ldifLine); 3900 } 3901 out(); 3902 } 3903 3904 3905 LDAPResult modifyResult; 3906 try 3907 { 3908 modifyResult = pool.modify(modifyRequest); 3909 } 3910 catch (final LDAPException le) 3911 { 3912 Debug.debugException(le); 3913 modifyResult = le.toLDAPResult(); 3914 } 3915 3916 3917 // Display information about the result. 3918 displayResult(modifyResult, useTransaction.isPresent()); 3919 3920 3921 // See if the modify operation succeeded or failed. If it failed, and we 3922 // should end all processing, then throw an exception. 3923 switch (modifyResult.getResultCode().intValue()) 3924 { 3925 case ResultCode.SUCCESS_INT_VALUE: 3926 case ResultCode.NO_OPERATION_INT_VALUE: 3927 break; 3928 3929 case ResultCode.ASSERTION_FAILED_INT_VALUE: 3930 writeRejectedChange(rejectWriter, 3931 INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyRequest.getDN(), 3932 String.valueOf(assertionFilter.getValue())), 3933 modifyRequest.toLDIFChangeRecord(), modifyResult); 3934 throw new LDAPException(modifyResult); 3935 3936 default: 3937 writeRejectedChange(rejectWriter, null, 3938 modifyRequest.toLDIFChangeRecord(), modifyResult); 3939 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 3940 { 3941 throw new LDAPException(modifyResult); 3942 } 3943 break; 3944 } 3945 3946 return modifyResult.getResultCode(); 3947 } 3948 3949 3950 3951 /** 3952 * Performs the appropriate processing for an LDIF modify DN change record. 3953 * 3954 * @param changeRecord The LDIF modify DN change record to process. 3955 * @param controls The set of controls to include in the request. 3956 * @param pool The connection pool to use to communicate with 3957 * the directory server. 3958 * @param multiUpdateRequests The list to which the request should be added 3959 * if it is to be processed as part of a 3960 * multi-update operation. It may be 3961 * {@code null} if the operation should not be 3962 * processed via the multi-update operation. 3963 * @param rejectWriter The LDIF writer to use for recording 3964 * information about rejected changes. It may be 3965 * {@code null} if no reject writer is 3966 * configured. 3967 * 3968 * @return The result code obtained from processing. 3969 * 3970 * @throws LDAPException If the operation did not complete successfully 3971 * and processing should not continue. 3972 */ 3973 private ResultCode doModifyDN(final LDIFModifyDNChangeRecord changeRecord, 3974 final List<Control> controls, 3975 final LDAPConnectionPool pool, 3976 final List<LDAPRequest> multiUpdateRequests, 3977 final LDIFWriter rejectWriter) 3978 throws LDAPException 3979 { 3980 // Create the modify DN request to process. 3981 final ModifyDNRequest modifyDNRequest = 3982 changeRecord.toModifyDNRequest(true); 3983 for (final Control c : controls) 3984 { 3985 modifyDNRequest.addControl(c); 3986 } 3987 3988 3989 // If the operation should be processed in a multi-update operation, then 3990 // just add the request to the list and return without doing anything else. 3991 if (multiUpdateErrorBehavior.isPresent()) 3992 { 3993 multiUpdateRequests.add(modifyDNRequest); 3994 commentToOut(INFO_LDAPMODIFY_MODIFY_DN_ADDED_TO_MULTI_UPDATE.get( 3995 modifyDNRequest.getDN())); 3996 return ResultCode.SUCCESS; 3997 } 3998 3999 4000 // Try to determine the new DN that the entry will have after the operation. 4001 DN newDN = null; 4002 try 4003 { 4004 newDN = changeRecord.getNewDN(); 4005 } 4006 catch (final Exception e) 4007 { 4008 Debug.debugException(e); 4009 4010 // This should only happen if the provided DN, new RDN, or new superior DN 4011 // was malformed. Although we could reject the operation now, we'll go 4012 // ahead and send the request to the server in case it has some special 4013 // handling for the DN. 4014 } 4015 4016 4017 // If the --dryRun argument was provided, then we'll stop here. 4018 if (dryRun.isPresent()) 4019 { 4020 if (modifyDNRequest.getNewSuperiorDN() == null) 4021 { 4022 if (newDN == null) 4023 { 4024 commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME.get( 4025 modifyDNRequest.getDN(), dryRun.getIdentifierString())); 4026 } 4027 else 4028 { 4029 commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME_TO.get( 4030 modifyDNRequest.getDN(), newDN.toString(), 4031 dryRun.getIdentifierString())); 4032 } 4033 } 4034 else 4035 { 4036 if (newDN == null) 4037 { 4038 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE.get( 4039 modifyDNRequest.getDN(), dryRun.getIdentifierString())); 4040 } 4041 else 4042 { 4043 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE_TO.get( 4044 modifyDNRequest.getDN(), newDN.toString(), 4045 dryRun.getIdentifierString())); 4046 } 4047 } 4048 return ResultCode.SUCCESS; 4049 } 4050 4051 4052 // Process the modify DN operation and get the result. 4053 final String currentDN = modifyDNRequest.getDN(); 4054 if (modifyDNRequest.getNewSuperiorDN() == null) 4055 { 4056 if (newDN == null) 4057 { 4058 commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY.get(currentDN)); 4059 } 4060 else 4061 { 4062 commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY_TO.get(currentDN, 4063 newDN.toString())); 4064 } 4065 } 4066 else 4067 { 4068 if (newDN == null) 4069 { 4070 commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY.get(currentDN)); 4071 } 4072 else 4073 { 4074 commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY_TO.get(currentDN, 4075 newDN.toString())); 4076 } 4077 } 4078 4079 if (verbose.isPresent()) 4080 { 4081 for (final String ldifLine : 4082 modifyDNRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 4083 { 4084 out(ldifLine); 4085 } 4086 out(); 4087 } 4088 4089 4090 LDAPResult modifyDNResult; 4091 try 4092 { 4093 modifyDNResult = pool.modifyDN(modifyDNRequest); 4094 } 4095 catch (final LDAPException le) 4096 { 4097 Debug.debugException(le); 4098 modifyDNResult = le.toLDAPResult(); 4099 } 4100 4101 4102 // Display information about the result. 4103 displayResult(modifyDNResult, useTransaction.isPresent()); 4104 4105 4106 // See if the modify DN operation succeeded or failed. If it failed, and we 4107 // should end all processing, then throw an exception. 4108 switch (modifyDNResult.getResultCode().intValue()) 4109 { 4110 case ResultCode.SUCCESS_INT_VALUE: 4111 case ResultCode.NO_OPERATION_INT_VALUE: 4112 break; 4113 4114 case ResultCode.ASSERTION_FAILED_INT_VALUE: 4115 writeRejectedChange(rejectWriter, 4116 INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyDNRequest.getDN(), 4117 String.valueOf(assertionFilter.getValue())), 4118 modifyDNRequest.toLDIFChangeRecord(), modifyDNResult); 4119 throw new LDAPException(modifyDNResult); 4120 4121 default: 4122 writeRejectedChange(rejectWriter, null, 4123 modifyDNRequest.toLDIFChangeRecord(), modifyDNResult); 4124 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 4125 { 4126 throw new LDAPException(modifyDNResult); 4127 } 4128 break; 4129 } 4130 4131 return modifyDNResult.getResultCode(); 4132 } 4133 4134 4135 4136 /** 4137 * Displays information about the provided result, including special 4138 * processing for a number of supported response controls. 4139 * 4140 * @param result The result to examine. 4141 * @param inTransaction Indicates whether the operation is part of a 4142 * transaction. 4143 */ 4144 private void displayResult(final LDAPResult result, 4145 final boolean inTransaction) 4146 { 4147 final ArrayList<String> resultLines = new ArrayList<>(10); 4148 ResultUtils.formatResult(resultLines, result, true, inTransaction, 0, 4149 WRAP_COLUMN); 4150 4151 if (result.getResultCode() == ResultCode.SUCCESS) 4152 { 4153 for (final String line : resultLines) 4154 { 4155 out(line); 4156 } 4157 out(); 4158 } 4159 else 4160 { 4161 for (final String line : resultLines) 4162 { 4163 err(line); 4164 } 4165 err(); 4166 } 4167 } 4168 4169 4170 4171 /** 4172 * Writes a line-wrapped, commented version of the provided message to 4173 * standard output. 4174 * 4175 * @param message The message to be written. 4176 */ 4177 private void commentToOut(final String message) 4178 { 4179 for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2)) 4180 { 4181 out("# ", line); 4182 } 4183 } 4184 4185 4186 4187 /** 4188 * Writes a line-wrapped, commented version of the provided message to 4189 * standard error. 4190 * 4191 * @param message The message to be written. 4192 */ 4193 private void commentToErr(final String message) 4194 { 4195 for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2)) 4196 { 4197 err("# ", line); 4198 } 4199 } 4200 4201 4202 4203 /** 4204 * Writes information about the rejected change to the reject writer. 4205 * 4206 * @param writer The LDIF writer to which the information should be 4207 * written. It may be {@code null} if no reject file is 4208 * configured. 4209 * @param comment The comment to include before the change record, in 4210 * addition to the comment generated from the provided 4211 * LDAP result. It may be {@code null} if no additional 4212 * comment should be included. 4213 * @param changeRecord The LDIF change record to be written. It must not 4214 * be {@code null}. 4215 * @param ldapResult The LDAP result for the failed operation. It must 4216 * not be {@code null}. 4217 */ 4218 private void writeRejectedChange(final LDIFWriter writer, 4219 final String comment, 4220 final LDIFChangeRecord changeRecord, 4221 final LDAPResult ldapResult) 4222 { 4223 if (writer == null) 4224 { 4225 return; 4226 } 4227 4228 4229 final StringBuilder buffer = new StringBuilder(); 4230 if (comment != null) 4231 { 4232 buffer.append(comment); 4233 buffer.append(StaticUtils.EOL); 4234 buffer.append(StaticUtils.EOL); 4235 } 4236 4237 final ArrayList<String> resultLines = new ArrayList<>(10); 4238 ResultUtils.formatResult(resultLines, ldapResult, false, false, 0, 0); 4239 for (final String resultLine : resultLines) 4240 { 4241 buffer.append(resultLine); 4242 buffer.append(StaticUtils.EOL); 4243 } 4244 4245 writeRejectedChange(writer, buffer.toString(), changeRecord); 4246 } 4247 4248 4249 4250 /** 4251 * Writes information about the rejected change to the reject writer. 4252 * 4253 * @param writer The LDIF writer to which the information should be 4254 * written. It may be {@code null} if no reject file is 4255 * configured. 4256 * @param comment The comment to include before the change record. It 4257 * may be {@code null} if no comment should be included. 4258 * @param changeRecord The LDIF change record to be written. It may be 4259 * {@code null} if only a comment should be written. 4260 */ 4261 void writeRejectedChange(final LDIFWriter writer, final String comment, 4262 final LDIFChangeRecord changeRecord) 4263 { 4264 if (writer == null) 4265 { 4266 return; 4267 } 4268 4269 if (rejectWritten.compareAndSet(false, true)) 4270 { 4271 try 4272 { 4273 writer.writeVersionHeader(); 4274 } 4275 catch (final Exception e) 4276 { 4277 Debug.debugException(e); 4278 } 4279 } 4280 4281 try 4282 { 4283 if (comment != null) 4284 { 4285 writer.writeComment(comment, true, false); 4286 } 4287 4288 if (changeRecord != null) 4289 { 4290 writer.writeChangeRecord(changeRecord); 4291 } 4292 } 4293 catch (final Exception e) 4294 { 4295 Debug.debugException(e); 4296 4297 commentToErr(ERR_LDAPMODIFY_UNABLE_TO_WRITE_REJECTED_CHANGE.get( 4298 rejectFile.getValue().getAbsolutePath(), 4299 StaticUtils.getExceptionMessage(e))); 4300 } 4301 } 4302 4303 4304 4305 /** 4306 * {@inheritDoc} 4307 */ 4308 @Override() 4309 public void handleUnsolicitedNotification(final LDAPConnection connection, 4310 final ExtendedResult notification) 4311 { 4312 final ArrayList<String> lines = new ArrayList<>(10); 4313 ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, 4314 WRAP_COLUMN); 4315 for (final String line : lines) 4316 { 4317 err(line); 4318 } 4319 err(); 4320 } 4321 4322 4323 4324 /** 4325 * {@inheritDoc} 4326 */ 4327 @Override() 4328 public LinkedHashMap<String[],String> getExampleUsages() 4329 { 4330 final LinkedHashMap<String[],String> examples = 4331 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 4332 4333 final String[] args1 = 4334 { 4335 "--hostname", "ldap.example.com", 4336 "--port", "389", 4337 "--bindDN", "uid=admin,dc=example,dc=com", 4338 "--bindPassword", "password", 4339 "--defaultAdd" 4340 }; 4341 examples.put(args1, INFO_LDAPMODIFY_EXAMPLE_1.get()); 4342 4343 final String[] args2 = 4344 { 4345 "--hostname", "ds1.example.com", 4346 "--port", "636", 4347 "--hostname", "ds2.example.com", 4348 "--port", "636", 4349 "--useSSL", 4350 "--bindDN", "uid=admin,dc=example,dc=com", 4351 "--bindPassword", "password", 4352 "--filename", "changes.ldif", 4353 "--modifyEntriesMatchingFilter", "(objectClass=person)", 4354 "--searchPageSize", "100" 4355 }; 4356 examples.put(args2, INFO_LDAPMODIFY_EXAMPLE_2.get()); 4357 4358 return examples; 4359 } 4360}