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