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