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