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