001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.Message; 029 import org.opends.messages.MessageBuilder; 030 031 import java.util.ArrayList; 032 import java.util.Iterator; 033 import java.util.List; 034 import org.opends.server.api.ClientConnection; 035 import org.opends.server.api.plugin.PluginResult; 036 import org.opends.server.protocols.asn1.ASN1OctetString; 037 import org.opends.server.types.operation.PostResponseModifyDNOperation; 038 import org.opends.server.types.operation.PreParseModifyDNOperation; 039 import static org.opends.server.core.CoreConstants.*; 040 import static org.opends.server.loggers.AccessLogger.*; 041 042 import org.opends.server.types.*; 043 import org.opends.server.workflowelement.localbackend.*; 044 045 import static org.opends.server.loggers.debug.DebugLogger.*; 046 047 import org.opends.server.loggers.debug.DebugLogger; 048 import org.opends.server.loggers.debug.DebugTracer; 049 import org.opends.server.loggers.ErrorLogger; 050 import static org.opends.messages.CoreMessages.*; 051 import static org.opends.server.util.StaticUtils.*; 052 053 054 /** 055 * This class defines an operation that may be used to alter the DN of an entry 056 * in the Directory Server. 057 */ 058 public class ModifyDNOperationBasis 059 extends AbstractOperation 060 implements ModifyDNOperation, 061 PreParseModifyDNOperation, 062 PostResponseModifyDNOperation 063 { 064 /** 065 * The tracer object for the debug logger. 066 */ 067 private static final DebugTracer TRACER = DebugLogger.getTracer(); 068 069 // Indicates whether to delete the old RDN value from the entry. 070 private boolean deleteOldRDN; 071 072 // The raw, unprocessed current DN of the entry as included in the request 073 // from the client. 074 private ByteString rawEntryDN; 075 076 // The raw, unprocessed newRDN as included in the request from the client. 077 private ByteString rawNewRDN; 078 079 // The raw, unprocessed newSuperior as included in the request from the 080 // client. 081 private ByteString rawNewSuperior; 082 083 // The current DN of the entry. 084 private DN entryDN; 085 086 // The new parent for the entry. 087 private DN newSuperior; 088 089 // The proxied authorization target DN for this operation. 090 private DN proxiedAuthorizationDN; 091 092 // The set of response controls for this modify DN operation. 093 private List<Control> responseControls; 094 095 // The set of modifications applied to attributes in the entry in the course 096 // of processing the modify DN. 097 private List<Modification> modifications; 098 099 // The change number that has been assigned to this operation. 100 private long changeNumber; 101 102 // The new RDN for the entry. 103 private RDN newRDN; 104 105 // The new entry DN 106 private DN newDN = null; 107 108 /** 109 * Creates a new modify DN operation with the provided information. 110 * 111 * @param clientConnection The client connection with which this operation 112 * is associated. 113 * @param operationID The operation ID for this operation. 114 * @param messageID The message ID of the request with which this 115 * operation is associated. 116 * @param requestControls The set of controls included in the request. 117 * @param rawEntryDN The raw, unprocessed entry DN as included in the 118 * client request. 119 * @param rawNewRDN The raw, unprocessed newRDN as included in the 120 * client request. 121 * @param deleteOldRDN Indicates whether to delete the old RDN value 122 * from the entry. 123 * @param rawNewSuperior The raw, unprocessed newSuperior as included in 124 * the client request. 125 */ 126 public ModifyDNOperationBasis(ClientConnection clientConnection, 127 long operationID, 128 int messageID, List<Control> requestControls, 129 ByteString rawEntryDN, ByteString rawNewRDN, 130 boolean deleteOldRDN, ByteString rawNewSuperior) 131 { 132 super(clientConnection, operationID, messageID, requestControls); 133 134 135 this.rawEntryDN = rawEntryDN; 136 this.rawNewRDN = rawNewRDN; 137 this.deleteOldRDN = deleteOldRDN; 138 this.rawNewSuperior = rawNewSuperior; 139 140 entryDN = null; 141 newRDN = null; 142 newSuperior = null; 143 responseControls = new ArrayList<Control>(); 144 cancelRequest = null; 145 modifications = null; 146 changeNumber = -1; 147 } 148 149 150 151 /** 152 * Creates a new modify DN operation with the provided information. 153 * 154 * @param clientConnection The client connection with which this operation 155 * is associated. 156 * @param operationID The operation ID for this operation. 157 * @param messageID The message ID of the request with which this 158 * operation is associated. 159 * @param requestControls The set of controls included in the request. 160 * @param entryDN The current entry DN for this modify DN 161 * operation. 162 * @param newRDN The new RDN for this modify DN operation. 163 * @param deleteOldRDN Indicates whether to delete the old RDN value 164 * from the entry. 165 * @param newSuperior The newSuperior DN for this modify DN operation. 166 */ 167 public ModifyDNOperationBasis(ClientConnection clientConnection, 168 long operationID, 169 int messageID, List<Control> requestControls, 170 DN entryDN, RDN newRDN, boolean deleteOldRDN, 171 DN newSuperior) 172 { 173 super(clientConnection, operationID, messageID, requestControls); 174 175 176 this.entryDN = entryDN; 177 this.newRDN = newRDN; 178 this.deleteOldRDN = deleteOldRDN; 179 this.newSuperior = newSuperior; 180 181 rawEntryDN = new ASN1OctetString(entryDN.toString()); 182 rawNewRDN = new ASN1OctetString(newRDN.toString()); 183 184 if (newSuperior == null) 185 { 186 rawNewSuperior = null; 187 } 188 else 189 { 190 rawNewSuperior = new ASN1OctetString(newSuperior.toString()); 191 } 192 193 responseControls = new ArrayList<Control>(); 194 cancelRequest = null; 195 modifications = null; 196 changeNumber = -1; 197 } 198 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 public final ByteString getRawEntryDN() 205 { 206 return rawEntryDN; 207 } 208 209 210 211 /** 212 * {@inheritDoc} 213 */ 214 public final void setRawEntryDN(ByteString rawEntryDN) 215 { 216 this.rawEntryDN = rawEntryDN; 217 218 entryDN = null; 219 } 220 221 222 223 /** 224 * {@inheritDoc} 225 */ 226 public final DN getEntryDN() 227 { 228 try 229 { 230 if (entryDN == null) 231 { 232 entryDN = DN.decode(rawEntryDN); 233 } 234 } 235 catch (DirectoryException de) 236 { 237 if (debugEnabled()) 238 { 239 TRACER.debugCaught(DebugLogLevel.ERROR, de); 240 } 241 setResultCode(de.getResultCode()); 242 appendErrorMessage(de.getMessageObject()); 243 } 244 return entryDN; 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 public final ByteString getRawNewRDN() 251 { 252 return rawNewRDN; 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 public final void setRawNewRDN(ByteString rawNewRDN) 259 { 260 this.rawNewRDN = rawNewRDN; 261 262 newRDN = null; 263 newDN = null; 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 public final RDN getNewRDN() 270 { 271 try 272 { 273 if (newRDN == null) 274 { 275 newRDN = RDN.decode(rawNewRDN.stringValue()); 276 } 277 } 278 catch (DirectoryException de) 279 { 280 if (debugEnabled()) 281 { 282 TRACER.debugCaught(DebugLogLevel.ERROR, de); 283 } 284 285 setResultCode(de.getResultCode()); 286 appendErrorMessage(de.getMessageObject()); 287 } 288 return newRDN; 289 } 290 291 292 /** 293 * {@inheritDoc} 294 */ 295 public final boolean deleteOldRDN() 296 { 297 return deleteOldRDN; 298 } 299 300 /** 301 * {@inheritDoc} 302 */ 303 public final void setDeleteOldRDN(boolean deleteOldRDN) 304 { 305 this.deleteOldRDN = deleteOldRDN; 306 } 307 308 /** 309 * {@inheritDoc} 310 */ 311 public final ByteString getRawNewSuperior() 312 { 313 return rawNewSuperior; 314 } 315 316 /** 317 * {@inheritDoc} 318 */ 319 public final void setRawNewSuperior(ByteString rawNewSuperior) 320 { 321 this.rawNewSuperior = rawNewSuperior; 322 323 newSuperior = null; 324 newDN = null; 325 } 326 327 /** 328 * {@inheritDoc} 329 */ 330 public final DN getNewSuperior() 331 { 332 if (rawNewSuperior == null) 333 { 334 newSuperior = null; 335 } 336 else 337 { 338 try 339 { 340 if (newSuperior == null) 341 { 342 newSuperior = DN.decode(rawNewSuperior); 343 } 344 } 345 catch (DirectoryException de) 346 { 347 if (debugEnabled()) 348 { 349 TRACER.debugCaught(DebugLogLevel.ERROR, de); 350 } 351 352 setResultCode(de.getResultCode()); 353 appendErrorMessage(de.getMessageObject()); 354 } 355 } 356 return newSuperior; 357 } 358 359 360 /** 361 * {@inheritDoc} 362 */ 363 public final List<Modification> getModifications() 364 { 365 return modifications; 366 } 367 368 369 /** 370 * {@inheritDoc} 371 */ 372 public final void addModification(Modification modification) 373 { 374 if (modifications == null) 375 { 376 modifications = new ArrayList<Modification>(); 377 } 378 if (modification != null) 379 { 380 modifications.add(modification); 381 } 382 } 383 384 385 /** 386 * {@inheritDoc} 387 */ 388 public final Entry getOriginalEntry() 389 { 390 return null; 391 } 392 393 394 /** 395 * {@inheritDoc} 396 */ 397 public final Entry getUpdatedEntry() 398 { 399 return null; 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 public final long getChangeNumber() 406 { 407 return changeNumber; 408 } 409 410 411 /** 412 * {@inheritDoc} 413 */ 414 public final void setChangeNumber(long changeNumber) 415 { 416 this.changeNumber = changeNumber; 417 } 418 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override() 424 public final OperationType getOperationType() 425 { 426 // Note that no debugging will be done in this method because it is a likely 427 // candidate for being called by the logging subsystem. 428 429 return OperationType.MODIFY_DN; 430 } 431 432 433 /** 434 * {@inheritDoc} 435 */ 436 @Override() 437 public final String[][] getRequestLogElements() 438 { 439 // Note that no debugging will be done in this method because it is a likely 440 // candidate for being called by the logging subsystem. 441 442 String newSuperiorStr; 443 if (rawNewSuperior == null) 444 { 445 newSuperiorStr = null; 446 } 447 else 448 { 449 newSuperiorStr = rawNewSuperior.stringValue(); 450 } 451 452 return new String[][] 453 { 454 new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) }, 455 new String[] { LOG_ELEMENT_NEW_RDN, String.valueOf(newRDN) }, 456 new String[] { LOG_ELEMENT_DELETE_OLD_RDN, 457 String.valueOf(deleteOldRDN) }, 458 new String[] { LOG_ELEMENT_NEW_SUPERIOR, newSuperiorStr } 459 }; 460 } 461 462 463 /** 464 * {@inheritDoc} 465 */ 466 @Override() 467 public final String[][] getResponseLogElements() 468 { 469 // Note that no debugging will be done in this method because it is a likely 470 // candidate for being called by the logging subsystem. 471 472 String resultCode = String.valueOf(getResultCode().getIntValue()); 473 474 String errorMessage; 475 MessageBuilder errorMessageBuffer = getErrorMessage(); 476 if (errorMessageBuffer == null) 477 { 478 errorMessage = null; 479 } 480 else 481 { 482 errorMessage = errorMessageBuffer.toString(); 483 } 484 485 String matchedDNStr; 486 DN matchedDN = getMatchedDN(); 487 if (matchedDN == null) 488 { 489 matchedDNStr = null; 490 } 491 else 492 { 493 matchedDNStr = matchedDN.toString(); 494 } 495 496 String referrals; 497 List<String> referralURLs = getReferralURLs(); 498 if ((referralURLs == null) || referralURLs.isEmpty()) 499 { 500 referrals = null; 501 } 502 else 503 { 504 StringBuilder buffer = new StringBuilder(); 505 Iterator<String> iterator = referralURLs.iterator(); 506 buffer.append(iterator.next()); 507 508 while (iterator.hasNext()) 509 { 510 buffer.append(", "); 511 buffer.append(iterator.next()); 512 } 513 514 referrals = buffer.toString(); 515 } 516 517 String processingTime = 518 String.valueOf(getProcessingTime()); 519 520 return new String[][] 521 { 522 new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, 523 new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, 524 new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, 525 new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, 526 new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } 527 }; 528 } 529 530 531 /** 532 * {@inheritDoc} 533 */ 534 public DN getProxiedAuthorizationDN() 535 { 536 return proxiedAuthorizationDN; 537 } 538 539 540 /** 541 * {@inheritDoc} 542 */ 543 @Override() 544 public final List<Control> getResponseControls() 545 { 546 return responseControls; 547 } 548 549 550 /** 551 * {@inheritDoc} 552 */ 553 @Override() 554 public final void addResponseControl(Control control) 555 { 556 responseControls.add(control); 557 } 558 559 560 /** 561 * {@inheritDoc} 562 */ 563 @Override() 564 public final void removeResponseControl(Control control) 565 { 566 responseControls.remove(control); 567 } 568 569 570 /** 571 * Performs the work of actually processing this operation. This 572 * should include all processing for the operation, including 573 * invoking plugins, logging messages, performing access control, 574 * managing synchronization, and any other work that might need to 575 * be done in the course of processing. 576 */ 577 public final void run() 578 { 579 setResultCode(ResultCode.UNDEFINED); 580 581 // Start the processing timer. 582 setProcessingStartTime(); 583 584 // Log the modify DN request message. 585 logModifyDNRequest(this); 586 587 // Get the plugin config manager that will be used for invoking plugins. 588 PluginConfigManager pluginConfigManager = 589 DirectoryServer.getPluginConfigManager(); 590 591 // This flag is set to true as soon as a workflow has been executed. 592 boolean workflowExecuted = false; 593 594 595 try 596 { 597 // Check for and handle a request to cancel this operation. 598 checkIfCanceled(false); 599 600 // Invoke the pre-parse modify DN plugins. 601 PluginResult.PreParse preParseResult = 602 pluginConfigManager.invokePreParseModifyDNPlugins(this); 603 604 if(!preParseResult.continueProcessing()) 605 { 606 setResultCode(preParseResult.getResultCode()); 607 appendErrorMessage(preParseResult.getErrorMessage()); 608 setMatchedDN(preParseResult.getMatchedDN()); 609 setReferralURLs(preParseResult.getReferralURLs()); 610 return; 611 } 612 613 // Check for and handle a request to cancel this operation. 614 checkIfCanceled(false); 615 616 // Process the entry DN, newRDN, and newSuperior elements from their raw 617 // forms as provided by the client to the forms required for the rest of 618 // the modify DN processing. 619 DN entryDN = getEntryDN(); 620 if (entryDN == null) 621 { 622 return; 623 } 624 625 // Retrieve the network group attached to the client connection 626 // and get a workflow to process the operation. 627 NetworkGroup ng = getClientConnection().getNetworkGroup(); 628 Workflow workflow = ng.getWorkflowCandidate(entryDN); 629 if (workflow == null) 630 { 631 // We have found no workflow for the requested base DN, just return 632 // a no such entry result code and stop the processing. 633 updateOperationErrMsgAndResCode(); 634 return; 635 } 636 workflow.execute(this); 637 workflowExecuted = true; 638 } 639 catch(CanceledOperationException coe) 640 { 641 if (debugEnabled()) 642 { 643 TRACER.debugCaught(DebugLogLevel.ERROR, coe); 644 } 645 646 setResultCode(ResultCode.CANCELED); 647 cancelResult = new CancelResult(ResultCode.CANCELED, null); 648 649 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 650 } 651 finally 652 { 653 // Stop the processing timer. 654 setProcessingStopTime(); 655 656 if(cancelRequest == null || cancelResult == null || 657 cancelResult.getResultCode() != ResultCode.CANCELED || 658 cancelRequest.notifyOriginalRequestor() || 659 DirectoryServer.notifyAbandonedOperations()) 660 { 661 clientConnection.sendResponse(this); 662 } 663 664 // Log the modify DN response. 665 logModifyDNResponse(this); 666 667 // Notifies any persistent searches that might be registered with the 668 // server. 669 notifyPersistentSearches(workflowExecuted); 670 671 // Invoke the post-response modify DN plugins. 672 invokePostResponsePlugins(workflowExecuted); 673 674 // If no cancel result, set it 675 if(cancelResult == null) 676 { 677 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 678 } 679 } 680 } 681 682 683 /** 684 * Invokes the post response plugins. If a workflow has been executed 685 * then invoke the post response plugins provided by the workflow 686 * elements of the worklfow, otherwise invoke the post reponse plugins 687 * that have been registered with the current operation. 688 * 689 * @param workflowExecuted <code>true</code> if a workflow has been 690 * executed 691 */ 692 private void invokePostResponsePlugins(boolean workflowExecuted) 693 { 694 // Get the plugin config manager that will be used for invoking plugins. 695 PluginConfigManager pluginConfigManager = 696 DirectoryServer.getPluginConfigManager(); 697 698 // Invoke the post response plugins 699 if (workflowExecuted) 700 { 701 // Invoke the post response plugins that have been registered by 702 // the workflow elements 703 List localOperations = 704 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 705 706 if (localOperations != null) 707 { 708 for (Object localOp : localOperations) 709 { 710 LocalBackendModifyDNOperation localOperation = 711 (LocalBackendModifyDNOperation)localOp; 712 pluginConfigManager.invokePostResponseModifyDNPlugins(localOperation); 713 } 714 } 715 } 716 else 717 { 718 // Invoke the post response plugins that have been registered with 719 // the current operation 720 pluginConfigManager.invokePostResponseModifyDNPlugins(this); 721 } 722 } 723 724 725 /** 726 * Notifies any persistent searches that might be registered with the server. 727 * If no workflow has been executed then don't notify persistent searches. 728 * 729 * @param workflowExecuted <code>true</code> if a workflow has been 730 * executed 731 */ 732 private void notifyPersistentSearches(boolean workflowExecuted) 733 { 734 if (! workflowExecuted) 735 { 736 return; 737 } 738 739 List localOperations = 740 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 741 742 if (localOperations != null) 743 { 744 for (Object localOperation : localOperations) 745 { 746 LocalBackendModifyDNOperation localOp = 747 (LocalBackendModifyDNOperation)localOperation; 748 // Notify any persistent searches that might be registered with 749 // the server. 750 if (getResultCode() == ResultCode.SUCCESS) 751 { 752 for (PersistentSearch persistentSearch : 753 DirectoryServer.getPersistentSearches()) 754 { 755 try 756 { 757 persistentSearch.processModifyDN( 758 localOp, 759 localOp.getOriginalEntry(), 760 localOp.getUpdatedEntry()); 761 } 762 catch (Exception e) 763 { 764 if (debugEnabled()) 765 { 766 TRACER.debugCaught(DebugLogLevel.ERROR, e); 767 } 768 769 Message message = ERR_MODDN_ERROR_NOTIFYING_PERSISTENT_SEARCH.get( 770 String.valueOf(persistentSearch), getExceptionMessage(e)); 771 ErrorLogger.logError(message); 772 773 DirectoryServer.deregisterPersistentSearch(persistentSearch); 774 } 775 } 776 } 777 } 778 } 779 } 780 781 782 /** 783 * Updates the error message and the result code of the operation. 784 * 785 * This method is called because no workflows were found to process 786 * the operation. 787 */ 788 private void updateOperationErrMsgAndResCode() 789 { 790 setResultCode(ResultCode.NO_SUCH_OBJECT); 791 appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get( 792 String.valueOf(entryDN))); 793 } 794 795 796 /** 797 * {@inheritDoc} 798 */ 799 @Override() 800 public final void toString(StringBuilder buffer) 801 { 802 buffer.append("ModifyDNOperation(connID="); 803 buffer.append(clientConnection.getConnectionID()); 804 buffer.append(", opID="); 805 buffer.append(operationID); 806 buffer.append(", dn="); 807 buffer.append(rawEntryDN); 808 buffer.append(", newRDN="); 809 buffer.append(rawNewRDN); 810 buffer.append(", deleteOldRDN="); 811 buffer.append(deleteOldRDN); 812 813 if (rawNewSuperior != null) 814 { 815 buffer.append(", newSuperior="); 816 buffer.append(rawNewSuperior); 817 } 818 buffer.append(")"); 819 } 820 821 822 /** 823 * {@inheritDoc} 824 */ 825 public void setProxiedAuthorizationDN(DN dn) 826 { 827 proxiedAuthorizationDN = dn; 828 } 829 830 831 /** 832 * {@inheritDoc} 833 */ 834 public DN getNewDN() 835 { 836 if (newDN == null) 837 { 838 // Construct the new DN to use for the entry. 839 DN parentDN = null; 840 if (newSuperior == null) 841 { 842 if (getEntryDN() != null) 843 { 844 parentDN = entryDN.getParentDNInSuffix(); 845 } 846 } 847 else 848 { 849 parentDN = newSuperior; 850 } 851 852 if ((parentDN == null) || parentDN.isNullDN()) 853 { 854 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 855 appendErrorMessage(ERR_MODDN_NO_PARENT.get(String.valueOf(entryDN))); 856 } 857 newDN = parentDN.concat(newRDN); 858 } 859 return newDN; 860 } 861 862 } 863