001/* 002 * Copyright 2007-2026 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2026 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2026 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042import java.util.Timer; 043import java.util.concurrent.LinkedBlockingQueue; 044import java.util.concurrent.TimeUnit; 045import java.util.logging.Level; 046 047import com.unboundid.asn1.ASN1Boolean; 048import com.unboundid.asn1.ASN1Buffer; 049import com.unboundid.asn1.ASN1BufferSequence; 050import com.unboundid.asn1.ASN1Element; 051import com.unboundid.asn1.ASN1OctetString; 052import com.unboundid.asn1.ASN1Sequence; 053import com.unboundid.ldap.protocol.LDAPMessage; 054import com.unboundid.ldap.protocol.LDAPResponse; 055import com.unboundid.ldap.protocol.ProtocolOp; 056import com.unboundid.ldif.LDIFModifyDNChangeRecord; 057import com.unboundid.util.Debug; 058import com.unboundid.util.InternalUseOnly; 059import com.unboundid.util.Mutable; 060import com.unboundid.util.NotNull; 061import com.unboundid.util.Nullable; 062import com.unboundid.util.StaticUtils; 063import com.unboundid.util.ThreadSafety; 064import com.unboundid.util.ThreadSafetyLevel; 065import com.unboundid.util.Validator; 066 067import static com.unboundid.ldap.sdk.LDAPMessages.*; 068 069 070 071/** 072 * This class implements the processing necessary to perform an LDAPv3 modify DN 073 * operation, which can be used to rename and/or move an entry or subtree in the 074 * directory. A modify DN request contains the DN of the target entry, the new 075 * RDN to use for that entry, and a flag which indicates whether to remove the 076 * current RDN attribute value(s) from the entry. It may optionally contain a 077 * new superior DN, which will cause the entry to be moved below that new parent 078 * entry. 079 * <BR><BR> 080 * Note that some directory servers may not support all possible uses of the 081 * modify DN operation. In particular, some servers may not support the use of 082 * a new superior DN, especially if it may cause the entry to be moved to a 083 * different database or another server. Also, some servers may not support 084 * renaming or moving non-leaf entries (i.e., entries that have one or more 085 * subordinates). 086 * <BR><BR> 087 * {@code ModifyDNRequest} objects are mutable and therefore can be altered and 088 * re-used for multiple requests. Note, however, that {@code ModifyDNRequest} 089 * objects are not threadsafe and therefore a single {@code ModifyDNRequest} 090 * object instance should not be used to process multiple requests at the same 091 * time. 092 * <BR><BR> 093 * <H2>Example</H2> 094 * The following example demonstrates the process for performing a modify DN 095 * operation. In this case, it will rename "ou=People,dc=example,dc=com" to 096 * "ou=Users,dc=example,dc=com". It will not move the entry below a new parent. 097 * <PRE> 098 * ModifyDNRequest modifyDNRequest = 099 * new ModifyDNRequest("ou=People,dc=example,dc=com", "ou=Users", true); 100 * LDAPResult modifyDNResult; 101 * 102 * try 103 * { 104 * modifyDNResult = connection.modifyDN(modifyDNRequest); 105 * // If we get here, the delete was successful. 106 * } 107 * catch (LDAPException le) 108 * { 109 * // The modify DN operation failed. 110 * modifyDNResult = le.toLDAPResult(); 111 * ResultCode resultCode = le.getResultCode(); 112 * String errorMessageFromServer = le.getDiagnosticMessage(); 113 * } 114 * </PRE> 115 */ 116@Mutable() 117@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 118public final class ModifyDNRequest 119 extends UpdatableLDAPRequest 120 implements ReadOnlyModifyDNRequest, ResponseAcceptor, ProtocolOp 121{ 122 /** 123 * The BER type for the new superior element. 124 */ 125 private static final byte NEW_SUPERIOR_TYPE = (byte) 0x80; 126 127 128 129 /** 130 * The serial version UID for this serializable class. 131 */ 132 private static final long serialVersionUID = -2325552729975091008L; 133 134 135 136 // The queue that will be used to receive response messages from the server. 137 @NotNull private final LinkedBlockingQueue<LDAPResponse> responseQueue = 138 new LinkedBlockingQueue<>(); 139 140 // Indicates whether to delete the current RDN value from the entry. 141 private boolean deleteOldRDN; 142 143 // The message ID from the last LDAP message sent from this request. 144 private int messageID = -1; 145 146 // The current DN of the entry to rename. 147 @NotNull private String dn; 148 149 // The new RDN to use for the entry. 150 @NotNull private String newRDN; 151 152 // The new superior DN for the entry. 153 @Nullable private String newSuperiorDN; 154 155 156 157 /** 158 * Creates a new modify DN request that will rename the entry but will not 159 * move it below a new entry. 160 * 161 * @param dn The current DN for the entry to rename. It must not 162 * be {@code null}. 163 * @param newRDN The new RDN for the target entry. It must not be 164 * {@code null}. 165 * @param deleteOldRDN Indicates whether to delete the current RDN value 166 * from the target entry. 167 */ 168 public ModifyDNRequest(@NotNull final String dn, @NotNull final String newRDN, 169 final boolean deleteOldRDN) 170 { 171 super(null); 172 173 Validator.ensureNotNull(dn, newRDN); 174 175 this.dn = dn; 176 this.newRDN = newRDN; 177 this.deleteOldRDN = deleteOldRDN; 178 179 newSuperiorDN = null; 180 } 181 182 183 184 /** 185 * Creates a new modify DN request that will rename the entry but will not 186 * move it below a new entry. 187 * 188 * @param dn The current DN for the entry to rename. It must not 189 * be {@code null}. 190 * @param newRDN The new RDN for the target entry. It must not be 191 * {@code null}. 192 * @param deleteOldRDN Indicates whether to delete the current RDN value 193 * from the target entry. 194 */ 195 public ModifyDNRequest(@NotNull final DN dn, @NotNull final RDN newRDN, 196 final boolean deleteOldRDN) 197 { 198 super(null); 199 200 Validator.ensureNotNull(dn, newRDN); 201 202 this.dn = dn.toString(); 203 this.newRDN = newRDN.toString(); 204 this.deleteOldRDN = deleteOldRDN; 205 206 newSuperiorDN = null; 207 } 208 209 210 211 /** 212 * Creates a new modify DN request that will rename the entry and will 213 * optionally move it below a new entry. 214 * 215 * @param dn The current DN for the entry to rename. It must not 216 * be {@code null}. 217 * @param newRDN The new RDN for the target entry. It must not be 218 * {@code null}. 219 * @param deleteOldRDN Indicates whether to delete the current RDN value 220 * from the target entry. 221 * @param newSuperiorDN The new superior DN for the entry. It may be 222 * {@code null} if the entry is not to be moved below a 223 * new parent. 224 */ 225 public ModifyDNRequest(@NotNull final String dn, @NotNull final String newRDN, 226 final boolean deleteOldRDN, 227 @Nullable final String newSuperiorDN) 228 { 229 super(null); 230 231 Validator.ensureNotNull(dn, newRDN); 232 233 this.dn = dn; 234 this.newRDN = newRDN; 235 this.deleteOldRDN = deleteOldRDN; 236 this.newSuperiorDN = newSuperiorDN; 237 } 238 239 240 241 /** 242 * Creates a new modify DN request that will rename the entry and will 243 * optionally move it below a new entry. 244 * 245 * @param dn The current DN for the entry to rename. It must not 246 * be {@code null}. 247 * @param newRDN The new RDN for the target entry. It must not be 248 * {@code null}. 249 * @param deleteOldRDN Indicates whether to delete the current RDN value 250 * from the target entry. 251 * @param newSuperiorDN The new superior DN for the entry. It may be 252 * {@code null} if the entry is not to be moved below a 253 * new parent. 254 */ 255 public ModifyDNRequest(@NotNull final DN dn, @NotNull final RDN newRDN, 256 final boolean deleteOldRDN, 257 @Nullable final DN newSuperiorDN) 258 { 259 super(null); 260 261 Validator.ensureNotNull(dn, newRDN); 262 263 this.dn = dn.toString(); 264 this.newRDN = newRDN.toString(); 265 this.deleteOldRDN = deleteOldRDN; 266 267 if (newSuperiorDN == null) 268 { 269 this.newSuperiorDN = null; 270 } 271 else 272 { 273 this.newSuperiorDN = newSuperiorDN.toString(); 274 } 275 } 276 277 278 279 /** 280 * Creates a new modify DN request that will rename the entry but will not 281 * move it below a new entry. 282 * 283 * @param dn The current DN for the entry to rename. It must not 284 * be {@code null}. 285 * @param newRDN The new RDN for the target entry. It must not be 286 * {@code null}. 287 * @param deleteOldRDN Indicates whether to delete the current RDN value 288 * from the target entry. 289 * @param controls The set of controls to include in the request. 290 */ 291 public ModifyDNRequest(@NotNull final String dn, @NotNull final String newRDN, 292 final boolean deleteOldRDN, 293 @Nullable final Control[] controls) 294 { 295 super(controls); 296 297 Validator.ensureNotNull(dn, newRDN); 298 299 this.dn = dn; 300 this.newRDN = newRDN; 301 this.deleteOldRDN = deleteOldRDN; 302 303 newSuperiorDN = null; 304 } 305 306 307 308 /** 309 * Creates a new modify DN request that will rename the entry but will not 310 * move it below a new entry. 311 * 312 * @param dn The current DN for the entry to rename. It must not 313 * be {@code null}. 314 * @param newRDN The new RDN for the target entry. It must not be 315 * {@code null}. 316 * @param deleteOldRDN Indicates whether to delete the current RDN value 317 * from the target entry. 318 * @param controls The set of controls to include in the request. 319 */ 320 public ModifyDNRequest(@NotNull final DN dn, @NotNull final RDN newRDN, 321 final boolean deleteOldRDN, 322 @Nullable final Control[] controls) 323 { 324 super(controls); 325 326 Validator.ensureNotNull(dn, newRDN); 327 328 this.dn = dn.toString(); 329 this.newRDN = newRDN.toString(); 330 this.deleteOldRDN = deleteOldRDN; 331 332 newSuperiorDN = null; 333 } 334 335 336 337 /** 338 * Creates a new modify DN request that will rename the entry and will 339 * optionally move it below a new entry. 340 * 341 * @param dn The current DN for the entry to rename. It must not 342 * be {@code null}. 343 * @param newRDN The new RDN for the target entry. It must not be 344 * {@code null}. 345 * @param deleteOldRDN Indicates whether to delete the current RDN value 346 * from the target entry. 347 * @param newSuperiorDN The new superior DN for the entry. It may be 348 * {@code null} if the entry is not to be moved below a 349 * new parent. 350 * @param controls The set of controls to include in the request. 351 */ 352 public ModifyDNRequest(@NotNull final String dn, @NotNull final String newRDN, 353 final boolean deleteOldRDN, 354 @Nullable final String newSuperiorDN, 355 @Nullable final Control[] controls) 356 { 357 super(controls); 358 359 Validator.ensureNotNull(dn, newRDN); 360 361 this.dn = dn; 362 this.newRDN = newRDN; 363 this.deleteOldRDN = deleteOldRDN; 364 this.newSuperiorDN = newSuperiorDN; 365 } 366 367 368 369 /** 370 * Creates a new modify DN request that will rename the entry and will 371 * optionally move it below a new entry. 372 * 373 * @param dn The current DN for the entry to rename. It must not 374 * be {@code null}. 375 * @param newRDN The new RDN for the target entry. It must not be 376 * {@code null}. 377 * @param deleteOldRDN Indicates whether to delete the current RDN value 378 * from the target entry. 379 * @param newSuperiorDN The new superior DN for the entry. It may be 380 * {@code null} if the entry is not to be moved below a 381 * new parent. 382 * @param controls The set of controls to include in the request. 383 */ 384 public ModifyDNRequest(@NotNull final DN dn, @NotNull final RDN newRDN, 385 final boolean deleteOldRDN, 386 @Nullable final DN newSuperiorDN, 387 @Nullable final Control[] controls) 388 { 389 super(controls); 390 391 Validator.ensureNotNull(dn, newRDN); 392 393 this.dn = dn.toString(); 394 this.newRDN = newRDN.toString(); 395 this.deleteOldRDN = deleteOldRDN; 396 397 if (newSuperiorDN == null) 398 { 399 this.newSuperiorDN = null; 400 } 401 else 402 { 403 this.newSuperiorDN = newSuperiorDN.toString(); 404 } 405 } 406 407 408 409 /** 410 * {@inheritDoc} 411 */ 412 @Override() 413 @NotNull() 414 public String getDN() 415 { 416 return dn; 417 } 418 419 420 421 /** 422 * Specifies the current DN of the entry to move/rename. 423 * 424 * @param dn The current DN of the entry to move/rename. It must not be 425 * {@code null}. 426 */ 427 public void setDN(@NotNull final String dn) 428 { 429 Validator.ensureNotNull(dn); 430 431 this.dn = dn; 432 } 433 434 435 436 /** 437 * Specifies the current DN of the entry to move/rename. 438 * 439 * @param dn The current DN of the entry to move/rename. It must not be 440 * {@code null}. 441 */ 442 public void setDN(@NotNull final DN dn) 443 { 444 Validator.ensureNotNull(dn); 445 446 this.dn = dn.toString(); 447 } 448 449 450 451 /** 452 * {@inheritDoc} 453 */ 454 @Override() 455 @NotNull() 456 public String getNewRDN() 457 { 458 return newRDN; 459 } 460 461 462 463 /** 464 * Specifies the new RDN for the entry. 465 * 466 * @param newRDN The new RDN for the entry. It must not be {@code null}. 467 */ 468 public void setNewRDN(@NotNull final String newRDN) 469 { 470 Validator.ensureNotNull(newRDN); 471 472 this.newRDN = newRDN; 473 } 474 475 476 477 /** 478 * Specifies the new RDN for the entry. 479 * 480 * @param newRDN The new RDN for the entry. It must not be {@code null}. 481 */ 482 public void setNewRDN(@NotNull final RDN newRDN) 483 { 484 Validator.ensureNotNull(newRDN); 485 486 this.newRDN = newRDN.toString(); 487 } 488 489 490 491 /** 492 * {@inheritDoc} 493 */ 494 @Override() 495 public boolean deleteOldRDN() 496 { 497 return deleteOldRDN; 498 } 499 500 501 502 /** 503 * Specifies whether the current RDN value should be removed from the entry. 504 * 505 * @param deleteOldRDN Specifies whether the current RDN value should be 506 * removed from the entry. 507 */ 508 public void setDeleteOldRDN(final boolean deleteOldRDN) 509 { 510 this.deleteOldRDN = deleteOldRDN; 511 } 512 513 514 515 /** 516 * {@inheritDoc} 517 */ 518 @Override() 519 @Nullable() 520 public String getNewSuperiorDN() 521 { 522 return newSuperiorDN; 523 } 524 525 526 527 /** 528 * Specifies the new superior DN for the entry. 529 * 530 * @param newSuperiorDN The new superior DN for the entry. It may be 531 * {@code null} if the entry is not to be removed below 532 * a new parent. 533 */ 534 public void setNewSuperiorDN(@Nullable final String newSuperiorDN) 535 { 536 this.newSuperiorDN = newSuperiorDN; 537 } 538 539 540 541 /** 542 * Specifies the new superior DN for the entry. 543 * 544 * @param newSuperiorDN The new superior DN for the entry. It may be 545 * {@code null} if the entry is not to be removed below 546 * a new parent. 547 */ 548 public void setNewSuperiorDN(@Nullable final DN newSuperiorDN) 549 { 550 if (newSuperiorDN == null) 551 { 552 this.newSuperiorDN = null; 553 } 554 else 555 { 556 this.newSuperiorDN = newSuperiorDN.toString(); 557 } 558 } 559 560 561 562 /** 563 * {@inheritDoc} 564 */ 565 @Override() 566 public byte getProtocolOpType() 567 { 568 return LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST; 569 } 570 571 572 573 /** 574 * {@inheritDoc} 575 */ 576 @Override() 577 public void writeTo(@NotNull final ASN1Buffer writer) 578 { 579 final ASN1BufferSequence requestSequence = 580 writer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST); 581 writer.addOctetString(dn); 582 writer.addOctetString(newRDN); 583 writer.addBoolean(deleteOldRDN); 584 585 if (newSuperiorDN != null) 586 { 587 writer.addOctetString(NEW_SUPERIOR_TYPE, newSuperiorDN); 588 } 589 requestSequence.end(); 590 } 591 592 593 594 /** 595 * Encodes the modify DN request protocol op to an ASN.1 element. 596 * 597 * @return The ASN.1 element with the encoded modify DN request protocol op. 598 */ 599 @Override() 600 @NotNull() 601 public ASN1Element encodeProtocolOp() 602 { 603 final ASN1Element[] protocolOpElements; 604 if (newSuperiorDN == null) 605 { 606 protocolOpElements = new ASN1Element[] 607 { 608 new ASN1OctetString(dn), 609 new ASN1OctetString(newRDN), 610 new ASN1Boolean(deleteOldRDN) 611 }; 612 } 613 else 614 { 615 protocolOpElements = new ASN1Element[] 616 { 617 new ASN1OctetString(dn), 618 new ASN1OctetString(newRDN), 619 new ASN1Boolean(deleteOldRDN), 620 new ASN1OctetString(NEW_SUPERIOR_TYPE, newSuperiorDN) 621 }; 622 } 623 624 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST, 625 protocolOpElements); 626 } 627 628 629 630 /** 631 * Sends this modify DN request to the directory server over the provided 632 * connection and returns the associated response. 633 * 634 * @param connection The connection to use to communicate with the directory 635 * server. 636 * @param depth The current referral depth for this request. It should 637 * always be one for the initial request, and should only 638 * be incremented when following referrals. 639 * 640 * @return An LDAP result object that provides information about the result 641 * of the modify DN processing. 642 * 643 * @throws LDAPException If a problem occurs while sending the request or 644 * reading the response. 645 */ 646 @Override() 647 @NotNull() 648 protected LDAPResult process(@NotNull final LDAPConnection connection, 649 final int depth) 650 throws LDAPException 651 { 652 setReferralDepth(depth); 653 654 if (connection.synchronousMode()) 655 { 656 return processSync(connection, depth); 657 } 658 659 final long requestTime = System.nanoTime(); 660 processAsync(connection, null); 661 662 try 663 { 664 // Wait for and process the response. 665 final LDAPResponse response; 666 try 667 { 668 final long responseTimeout = getResponseTimeoutMillis(connection); 669 if (responseTimeout > 0) 670 { 671 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS); 672 } 673 else 674 { 675 response = responseQueue.take(); 676 } 677 } 678 catch (final InterruptedException ie) 679 { 680 Debug.debugException(ie); 681 Thread.currentThread().interrupt(); 682 throw new LDAPException(ResultCode.LOCAL_ERROR, 683 ERR_MODDN_INTERRUPTED.get(connection.getHostPort()), ie); 684 } 685 686 return handleResponse(connection, response, requestTime, depth); 687 } 688 finally 689 { 690 connection.deregisterResponseAcceptor(messageID); 691 } 692 } 693 694 695 696 /** 697 * Sends this modify DN request to the directory server over the provided 698 * connection and returns the message ID for the request. 699 * 700 * @param connection The connection to use to communicate with the 701 * directory server. 702 * @param resultListener The async result listener that is to be notified 703 * when the response is received. It may be 704 * {@code null} only if the result is to be processed 705 * by this class. 706 * 707 * @return The async request ID created for the operation, or {@code null} if 708 * the provided {@code resultListener} is {@code null} and the 709 * operation will not actually be processed asynchronously. 710 * 711 * @throws LDAPException If a problem occurs while sending the request. 712 */ 713 @Nullable() 714 AsyncRequestID processAsync(@NotNull final LDAPConnection connection, 715 @Nullable final AsyncResultListener resultListener) 716 throws LDAPException 717 { 718 // Create the LDAP message. 719 messageID = connection.nextMessageID(); 720 final LDAPMessage message = new LDAPMessage(messageID, this, getControls()); 721 722 723 // If the provided async result listener is {@code null}, then we'll use 724 // this class as the message acceptor. Otherwise, create an async helper 725 // and use it as the message acceptor. 726 final AsyncRequestID asyncRequestID; 727 final long timeout = getResponseTimeoutMillis(connection); 728 if (resultListener == null) 729 { 730 asyncRequestID = null; 731 connection.registerResponseAcceptor(messageID, this); 732 } 733 else 734 { 735 final AsyncHelper helper = new AsyncHelper(connection, 736 OperationType.MODIFY_DN, messageID, resultListener, 737 getIntermediateResponseListener()); 738 connection.registerResponseAcceptor(messageID, helper); 739 asyncRequestID = helper.getAsyncRequestID(); 740 741 if (timeout > 0L) 742 { 743 final Timer timer = connection.getTimer(); 744 final AsyncTimeoutTimerTask timerTask = 745 new AsyncTimeoutTimerTask(helper); 746 timer.schedule(timerTask, timeout); 747 asyncRequestID.setTimerTask(timerTask); 748 } 749 } 750 751 752 // Send the request to the server. 753 try 754 { 755 Debug.debugLDAPRequest(Level.INFO, this, messageID, connection); 756 757 final LDAPConnectionLogger logger = 758 connection.getConnectionOptions().getConnectionLogger(); 759 if (logger != null) 760 { 761 logger.logModifyDNRequest(connection, messageID, this); 762 } 763 764 connection.getConnectionStatistics().incrementNumModifyDNRequests(); 765 connection.sendMessage(message, timeout); 766 return asyncRequestID; 767 } 768 catch (final LDAPException le) 769 { 770 Debug.debugException(le); 771 772 connection.deregisterResponseAcceptor(messageID); 773 throw le; 774 } 775 } 776 777 778 779 /** 780 * Processes this modify DN operation in synchronous mode, in which the same 781 * thread will send the request and read the response. 782 * 783 * @param connection The connection to use to communicate with the directory 784 * server. 785 * @param depth The current referral depth for this request. It should 786 * always be one for the initial request, and should only 787 * be incremented when following referrals. 788 * 789 * @return An LDAP result object that provides information about the result 790 * of the modify DN processing. 791 * 792 * @throws LDAPException If a problem occurs while sending the request or 793 * reading the response. 794 */ 795 @NotNull() 796 private LDAPResult processSync(@NotNull final LDAPConnection connection, 797 final int depth) 798 throws LDAPException 799 { 800 // Create the LDAP message. 801 messageID = connection.nextMessageID(); 802 final LDAPMessage message = 803 new LDAPMessage(messageID, this, getControls()); 804 805 806 // Send the request to the server. 807 final long requestTime = System.nanoTime(); 808 Debug.debugLDAPRequest(Level.INFO, this, messageID, connection); 809 810 final LDAPConnectionLogger logger = 811 connection.getConnectionOptions().getConnectionLogger(); 812 if (logger != null) 813 { 814 logger.logModifyDNRequest(connection, messageID, this); 815 } 816 817 connection.getConnectionStatistics().incrementNumModifyDNRequests(); 818 connection.sendMessage(message, getResponseTimeoutMillis(connection)); 819 820 while (true) 821 { 822 final LDAPResponse response; 823 try 824 { 825 response = connection.readResponse(messageID); 826 } 827 catch (final LDAPException le) 828 { 829 Debug.debugException(le); 830 831 if ((le.getResultCode() == ResultCode.TIMEOUT) && 832 connection.getConnectionOptions().abandonOnTimeout()) 833 { 834 connection.abandon(messageID); 835 } 836 837 throw le; 838 } 839 840 if (response instanceof IntermediateResponse) 841 { 842 final IntermediateResponseListener listener = 843 getIntermediateResponseListener(); 844 if (listener != null) 845 { 846 listener.intermediateResponseReturned( 847 (IntermediateResponse) response); 848 } 849 } 850 else 851 { 852 return handleResponse(connection, response, requestTime, depth); 853 } 854 } 855 } 856 857 858 859 /** 860 * Performs the necessary processing for handling a response. 861 * 862 * @param connection The connection used to read the response. 863 * @param response The response to be processed. 864 * @param requestTime The time the request was sent to the server. 865 * @param depth The current referral depth for this request. It 866 * should always be one for the initial request, and 867 * should only be incremented when following referrals. 868 * 869 * @return The modify DN result. 870 * 871 * @throws LDAPException If a problem occurs. 872 */ 873 @NotNull() 874 private LDAPResult handleResponse(@NotNull final LDAPConnection connection, 875 @Nullable final LDAPResponse response, 876 final long requestTime, final int depth) 877 throws LDAPException 878 { 879 if (response == null) 880 { 881 final long waitTime = 882 StaticUtils.nanosToMillis(System.nanoTime() - requestTime); 883 if (connection.getConnectionOptions().abandonOnTimeout()) 884 { 885 connection.abandon(messageID); 886 } 887 888 throw new LDAPException(ResultCode.TIMEOUT, 889 ERR_MODIFY_DN_CLIENT_TIMEOUT.get(waitTime, messageID, dn, 890 connection.getHostPort())); 891 } 892 893 connection.getConnectionStatistics().incrementNumModifyDNResponses( 894 System.nanoTime() - requestTime); 895 if (response instanceof ConnectionClosedResponse) 896 { 897 // The connection was closed while waiting for the response. 898 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 899 final String message = ccr.getMessage(); 900 if (message == null) 901 { 902 throw new LDAPException(ccr.getResultCode(), 903 ERR_CONN_CLOSED_WAITING_FOR_MODIFY_DN_RESPONSE.get( 904 connection.getHostPort(), toString())); 905 } 906 else 907 { 908 throw new LDAPException(ccr.getResultCode(), 909 ERR_CONN_CLOSED_WAITING_FOR_MODIFY_DN_RESPONSE_WITH_MESSAGE.get( 910 connection.getHostPort(), toString(), message)); 911 } 912 } 913 914 final LDAPResult result = (LDAPResult) response; 915 if ((result.getResultCode().equals(ResultCode.REFERRAL)) && 916 followReferrals(connection)) 917 { 918 if (depth >= connection.getConnectionOptions().getReferralHopLimit()) 919 { 920 return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED, 921 ERR_TOO_MANY_REFERRALS.get(), 922 result.getMatchedDN(), result.getReferralURLs(), 923 result.getResponseControls()); 924 } 925 926 return ReferralHelper.handleReferral(this, result, connection); 927 } 928 else 929 { 930 return result; 931 } 932 } 933 934 935 936 /** 937 * {@inheritDoc} 938 */ 939 @InternalUseOnly() 940 @Override() 941 public void responseReceived(@NotNull final LDAPResponse response) 942 throws LDAPException 943 { 944 try 945 { 946 responseQueue.put(response); 947 } 948 catch (final Exception e) 949 { 950 Debug.debugException(e); 951 952 if (e instanceof InterruptedException) 953 { 954 Thread.currentThread().interrupt(); 955 } 956 957 throw new LDAPException(ResultCode.LOCAL_ERROR, 958 ERR_EXCEPTION_HANDLING_RESPONSE.get( 959 StaticUtils.getExceptionMessage(e)), e); 960 } 961 } 962 963 964 965 /** 966 * {@inheritDoc} 967 */ 968 @Override() 969 public int getLastMessageID() 970 { 971 return messageID; 972 } 973 974 975 976 /** 977 * {@inheritDoc} 978 */ 979 @Override() 980 @NotNull() 981 public OperationType getOperationType() 982 { 983 return OperationType.MODIFY_DN; 984 } 985 986 987 988 /** 989 * {@inheritDoc} 990 */ 991 @Override() 992 @NotNull() 993 public ModifyDNRequest duplicate() 994 { 995 return duplicate(getControls()); 996 } 997 998 999 1000 /** 1001 * {@inheritDoc} 1002 */ 1003 @Override() 1004 @NotNull() 1005 public ModifyDNRequest duplicate(@NotNull final Control[] controls) 1006 { 1007 final ModifyDNRequest r = new ModifyDNRequest(dn, newRDN, deleteOldRDN, 1008 newSuperiorDN, controls); 1009 1010 if (followReferralsInternal() != null) 1011 { 1012 r.setFollowReferrals(followReferralsInternal()); 1013 } 1014 1015 if (getReferralConnectorInternal() != null) 1016 { 1017 r.setReferralConnector(getReferralConnectorInternal()); 1018 } 1019 1020 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1021 r.setIntermediateResponseListener(getIntermediateResponseListener()); 1022 r.setReferralDepth(getReferralDepth()); 1023 1024 return r; 1025 } 1026 1027 1028 1029 /** 1030 * {@inheritDoc} 1031 */ 1032 @Override() 1033 @NotNull() 1034 public LDIFModifyDNChangeRecord toLDIFChangeRecord() 1035 { 1036 return new LDIFModifyDNChangeRecord(this); 1037 } 1038 1039 1040 1041 /** 1042 * {@inheritDoc} 1043 */ 1044 @Override() 1045 @NotNull() 1046 public String[] toLDIF() 1047 { 1048 return toLDIFChangeRecord().toLDIF(); 1049 } 1050 1051 1052 1053 /** 1054 * {@inheritDoc} 1055 */ 1056 @Override() 1057 @NotNull() 1058 public String toLDIFString() 1059 { 1060 return toLDIFChangeRecord().toLDIFString(); 1061 } 1062 1063 1064 1065 /** 1066 * {@inheritDoc} 1067 */ 1068 @Override() 1069 public void toString(@NotNull final StringBuilder buffer) 1070 { 1071 buffer.append("ModifyDNRequest(dn='"); 1072 buffer.append(dn); 1073 buffer.append("', newRDN='"); 1074 buffer.append(newRDN); 1075 buffer.append("', deleteOldRDN="); 1076 buffer.append(deleteOldRDN); 1077 1078 if (newSuperiorDN != null) 1079 { 1080 buffer.append(", newSuperiorDN='"); 1081 buffer.append(newSuperiorDN); 1082 buffer.append('\''); 1083 } 1084 1085 final Control[] controls = getControls(); 1086 if (controls.length > 0) 1087 { 1088 buffer.append(", controls={"); 1089 for (int i=0; i < controls.length; i++) 1090 { 1091 if (i > 0) 1092 { 1093 buffer.append(", "); 1094 } 1095 1096 buffer.append(controls[i]); 1097 } 1098 buffer.append('}'); 1099 } 1100 1101 buffer.append(')'); 1102 } 1103 1104 1105 1106 /** 1107 * {@inheritDoc} 1108 */ 1109 @Override() 1110 public void toCode(@NotNull final List<String> lineList, 1111 @NotNull final String requestID, 1112 final int indentSpaces, final boolean includeProcessing) 1113 { 1114 // Create the request variable. 1115 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(4); 1116 constructorArgs.add(ToCodeArgHelper.createString(dn, "Current DN")); 1117 constructorArgs.add(ToCodeArgHelper.createString(newRDN, "New RDN")); 1118 constructorArgs.add(ToCodeArgHelper.createBoolean(deleteOldRDN, 1119 "Delete Old RDN Value(s)")); 1120 1121 if (newSuperiorDN != null) 1122 { 1123 constructorArgs.add(ToCodeArgHelper.createString(newSuperiorDN, 1124 "New Superior Entry DN")); 1125 } 1126 1127 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "ModifyDNRequest", 1128 requestID + "Request", "new ModifyDNRequest", constructorArgs); 1129 1130 1131 // If there are any controls, then add them to the request. 1132 for (final Control c : getControls()) 1133 { 1134 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1135 requestID + "Request.addControl", 1136 ToCodeArgHelper.createControl(c, null)); 1137 } 1138 1139 1140 // Add lines for processing the request and obtaining the result. 1141 if (includeProcessing) 1142 { 1143 // Generate a string with the appropriate indent. 1144 final StringBuilder buffer = new StringBuilder(); 1145 for (int i=0; i < indentSpaces; i++) 1146 { 1147 buffer.append(' '); 1148 } 1149 final String indent = buffer.toString(); 1150 1151 lineList.add(""); 1152 lineList.add(indent + "try"); 1153 lineList.add(indent + '{'); 1154 lineList.add(indent + " LDAPResult " + requestID + 1155 "Result = connection.modifyDN(" + requestID + "Request);"); 1156 lineList.add(indent + " // The modify DN was processed successfully."); 1157 lineList.add(indent + '}'); 1158 lineList.add(indent + "catch (LDAPException e)"); 1159 lineList.add(indent + '{'); 1160 lineList.add(indent + " // The modify DN failed. Maybe the following " + 1161 "will help explain why."); 1162 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 1163 lineList.add(indent + " String message = e.getMessage();"); 1164 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 1165 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 1166 lineList.add(indent + " Control[] responseControls = " + 1167 "e.getResponseControls();"); 1168 lineList.add(indent + '}'); 1169 } 1170 } 1171}