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 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 029 import org.opends.messages.MessageBuilder; 030 import org.opends.messages.Message; 031 032 033 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ENTRY_DN; 034 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; 035 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN; 036 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; 037 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_REFERRAL_URLS; 038 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; 039 import static org.opends.server.loggers.AccessLogger.logModifyRequest; 040 import static org.opends.server.loggers.AccessLogger.logModifyResponse; 041 import static org.opends.server.loggers.ErrorLogger.logError; 042 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; 043 import static org.opends.messages.CoreMessages.*; 044 import static org.opends.server.util.StaticUtils.getExceptionMessage; 045 046 import java.util.ArrayList; 047 import java.util.Iterator; 048 import java.util.List; 049 050 import org.opends.server.api.ClientConnection; 051 import org.opends.server.api.plugin.PluginResult; 052 import org.opends.server.loggers.debug.DebugLogger; 053 import org.opends.server.loggers.debug.DebugTracer; 054 import org.opends.server.protocols.asn1.ASN1OctetString; 055 import org.opends.server.protocols.ldap.LDAPAttribute; 056 import org.opends.server.protocols.ldap.LDAPModification; 057 import org.opends.server.types.*; 058 import org.opends.server.types.operation.PostResponseModifyOperation; 059 import org.opends.server.types.operation.PreParseModifyOperation; 060 import org.opends.server.workflowelement.localbackend.*; 061 062 063 064 /** 065 * This class defines an operation that may be used to modify an entry in the 066 * Directory Server. 067 */ 068 public class ModifyOperationBasis 069 extends AbstractOperation implements ModifyOperation, 070 PreParseModifyOperation, 071 PostResponseModifyOperation 072 { 073 074 /** 075 * The tracer object for the debug logger. 076 */ 077 private static final DebugTracer TRACER = DebugLogger.getTracer(); 078 079 // The raw, unprocessed entry DN as included by the client request. 080 private ByteString rawEntryDN; 081 082 // The DN of the entry for the modify operation. 083 private DN entryDN; 084 085 // The proxied authorization target DN for this operation. 086 private DN proxiedAuthorizationDN; 087 088 // The set of response controls for this modify operation. 089 private List<Control> responseControls; 090 091 // The raw, unprocessed set of modifications as included in the client 092 // request. 093 private List<RawModification> rawModifications; 094 095 // The set of modifications for this modify operation. 096 private List<Modification> modifications; 097 098 // The change number that has been assigned to this operation. 099 private long changeNumber; 100 101 /** 102 * Creates a new modify operation with the provided information. 103 * 104 * @param clientConnection The client connection with which this operation 105 * is associated. 106 * @param operationID The operation ID for this operation. 107 * @param messageID The message ID of the request with which this 108 * operation is associated. 109 * @param requestControls The set of controls included in the request. 110 * @param rawEntryDN The raw, unprocessed DN of the entry to modify, 111 * as included in the client request. 112 * @param rawModifications The raw, unprocessed set of modifications for 113 * this modify operation as included in the client 114 * request. 115 */ 116 public ModifyOperationBasis(ClientConnection clientConnection, 117 long operationID, 118 int messageID, List<Control> requestControls, 119 ByteString rawEntryDN, 120 List<RawModification> rawModifications) 121 { 122 super(clientConnection, operationID, messageID, requestControls); 123 124 125 this.rawEntryDN = rawEntryDN; 126 this.rawModifications = rawModifications; 127 128 entryDN = null; 129 modifications = null; 130 responseControls = new ArrayList<Control>(); 131 cancelRequest = null; 132 } 133 134 /** 135 * Creates a new modify operation with the provided information. 136 * 137 * @param clientConnection The client connection with which this operation 138 * is associated. 139 * @param operationID The operation ID for this operation. 140 * @param messageID The message ID of the request with which this 141 * operation is associated. 142 * @param requestControls The set of controls included in the request. 143 * @param entryDN The entry DN for the modify operation. 144 * @param modifications The set of modifications for this modify 145 * operation. 146 */ 147 public ModifyOperationBasis(ClientConnection clientConnection, 148 long operationID, 149 int messageID, List<Control> requestControls, 150 DN entryDN, List<Modification> modifications) 151 { 152 super(clientConnection, operationID, messageID, requestControls); 153 154 155 this.entryDN = entryDN; 156 this.modifications = modifications; 157 158 rawEntryDN = new ASN1OctetString(entryDN.toString()); 159 160 rawModifications = new ArrayList<RawModification>(modifications.size()); 161 for (Modification m : modifications) 162 { 163 rawModifications.add(new LDAPModification(m.getModificationType(), 164 new LDAPAttribute(m.getAttribute()))); 165 } 166 167 responseControls = new ArrayList<Control>(); 168 cancelRequest = null; 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 public final ByteString getRawEntryDN() 175 { 176 return rawEntryDN; 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 public final void setRawEntryDN(ByteString rawEntryDN) 183 { 184 this.rawEntryDN = rawEntryDN; 185 186 entryDN = null; 187 } 188 189 /** 190 * {@inheritDoc} 191 */ 192 public final DN getEntryDN() 193 { 194 if (entryDN == null){ 195 try { 196 entryDN = DN.decode(rawEntryDN); 197 } 198 catch (DirectoryException de) { 199 if (debugEnabled()) 200 { 201 TRACER.debugCaught(DebugLogLevel.ERROR, de); 202 } 203 204 setResultCode(de.getResultCode()); 205 appendErrorMessage(de.getMessageObject()); 206 } 207 } 208 return entryDN; 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 public final List<RawModification> getRawModifications() 215 { 216 return rawModifications; 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 public final void addRawModification(RawModification rawModification) 223 { 224 rawModifications.add(rawModification); 225 226 modifications = null; 227 } 228 229 /** 230 * {@inheritDoc} 231 */ 232 public final void setRawModifications(List<RawModification> rawModifications) 233 { 234 this.rawModifications = rawModifications; 235 236 modifications = null; 237 } 238 239 /** 240 * {@inheritDoc} 241 */ 242 public final List<Modification> getModifications() 243 { 244 if (modifications == null) 245 { 246 modifications = new ArrayList<Modification>(rawModifications.size()); 247 try { 248 for (RawModification m : rawModifications) 249 { 250 modifications.add(m.toModification()); 251 } 252 } 253 catch (LDAPException le) 254 { 255 if (debugEnabled()) 256 { 257 TRACER.debugCaught(DebugLogLevel.ERROR, le); 258 } 259 setResultCode(ResultCode.valueOf(le.getResultCode())); 260 appendErrorMessage(le.getMessageObject()); 261 modifications = null; 262 } 263 } 264 return modifications; 265 } 266 267 /** 268 * {@inheritDoc} 269 */ 270 public final void addModification(Modification modification) 271 throws DirectoryException 272 { 273 modifications.add(modification); 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 public final OperationType getOperationType() 280 { 281 // Note that no debugging will be done in this method because it is a likely 282 // candidate for being called by the logging subsystem. 283 284 return OperationType.MODIFY; 285 } 286 287 /** 288 * {@inheritDoc} 289 */ 290 public final String[][] getRequestLogElements() 291 { 292 // Note that no debugging will be done in this method because it is a likely 293 // candidate for being called by the logging subsystem. 294 295 return new String[][] 296 { 297 new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } 298 }; 299 } 300 301 /** 302 * {@inheritDoc} 303 */ 304 public final String[][] getResponseLogElements() 305 { 306 // Note that no debugging will be done in this method because it is a likely 307 // candidate for being called by the logging subsystem. 308 309 String resultCode = String.valueOf(getResultCode().getIntValue()); 310 311 String errorMessage; 312 MessageBuilder errorMessageBuffer = getErrorMessage(); 313 if (errorMessageBuffer == null) 314 { 315 errorMessage = null; 316 } 317 else 318 { 319 errorMessage = errorMessageBuffer.toString(); 320 } 321 322 String matchedDNStr; 323 DN matchedDN = getMatchedDN(); 324 if (matchedDN == null) 325 { 326 matchedDNStr = null; 327 } 328 else 329 { 330 matchedDNStr = matchedDN.toString(); 331 } 332 333 String referrals; 334 List<String> referralURLs = getReferralURLs(); 335 if ((referralURLs == null) || referralURLs.isEmpty()) 336 { 337 referrals = null; 338 } 339 else 340 { 341 StringBuilder buffer = new StringBuilder(); 342 Iterator<String> iterator = referralURLs.iterator(); 343 buffer.append(iterator.next()); 344 345 while (iterator.hasNext()) 346 { 347 buffer.append(", "); 348 buffer.append(iterator.next()); 349 } 350 351 referrals = buffer.toString(); 352 } 353 354 String processingTime = 355 String.valueOf(getProcessingTime()); 356 357 return new String[][] 358 { 359 new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, 360 new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, 361 new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, 362 new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, 363 new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } 364 }; 365 } 366 367 /** 368 * {@inheritDoc} 369 */ 370 public DN getProxiedAuthorizationDN() 371 { 372 return proxiedAuthorizationDN; 373 } 374 375 /** 376 * {@inheritDoc} 377 */ 378 public final List<Control> getResponseControls() 379 { 380 return responseControls; 381 } 382 383 /** 384 * {@inheritDoc} 385 */ 386 public final void addResponseControl(Control control) 387 { 388 responseControls.add(control); 389 } 390 391 /** 392 * {@inheritDoc} 393 */ 394 public final void removeResponseControl(Control control) 395 { 396 responseControls.remove(control); 397 } 398 399 /** 400 * {@inheritDoc} 401 */ 402 public final void toString(StringBuilder buffer) 403 { 404 buffer.append("ModifyOperation(connID="); 405 buffer.append(clientConnection.getConnectionID()); 406 buffer.append(", opID="); 407 buffer.append(operationID); 408 buffer.append(", dn="); 409 buffer.append(rawEntryDN); 410 buffer.append(")"); 411 } 412 413 /** 414 * {@inheritDoc} 415 */ 416 public final long getChangeNumber(){ 417 return changeNumber; 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 public void setChangeNumber(long changeNumber) 424 { 425 this.changeNumber = changeNumber; 426 } 427 428 /** 429 * {@inheritDoc} 430 */ 431 public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) 432 { 433 this.proxiedAuthorizationDN = proxiedAuthorizationDN; 434 } 435 436 /** 437 * {@inheritDoc} 438 */ 439 public final void run() 440 { 441 setResultCode(ResultCode.UNDEFINED); 442 443 // Start the processing timer. 444 setProcessingStartTime(); 445 446 // Log the modify request message. 447 logModifyRequest(this); 448 449 // Get the plugin config manager that will be used for invoking plugins. 450 PluginConfigManager pluginConfigManager = 451 DirectoryServer.getPluginConfigManager(); 452 453 // This flag is set to true as soon as a workflow has been executed. 454 boolean workflowExecuted = false; 455 456 457 try 458 { 459 // Check for and handle a request to cancel this operation. 460 checkIfCanceled(false); 461 462 // Invoke the pre-parse modify plugins. 463 PluginResult.PreParse preParseResult = 464 pluginConfigManager.invokePreParseModifyPlugins(this); 465 466 if(!preParseResult.continueProcessing()) 467 { 468 setResultCode(preParseResult.getResultCode()); 469 appendErrorMessage(preParseResult.getErrorMessage()); 470 setMatchedDN(preParseResult.getMatchedDN()); 471 setReferralURLs(preParseResult.getReferralURLs()); 472 return; 473 } 474 475 // Check for and handle a request to cancel this operation. 476 checkIfCanceled(false); 477 478 479 // Process the entry DN to convert it from the raw form to the form 480 // required for the rest of the modify processing. 481 DN entryDN = getEntryDN(); 482 if (entryDN == null){ 483 return; 484 } 485 486 487 // Retrieve the network group attached to the client connection 488 // and get a workflow to process the operation. 489 NetworkGroup ng = getClientConnection().getNetworkGroup(); 490 Workflow workflow = ng.getWorkflowCandidate(entryDN); 491 if (workflow == null) 492 { 493 // We have found no workflow for the requested base DN, just return 494 // a no such entry result code and stop the processing. 495 updateOperationErrMsgAndResCode(); 496 return; 497 } 498 workflow.execute(this); 499 workflowExecuted = true; 500 501 } 502 catch(CanceledOperationException coe) 503 { 504 if (debugEnabled()) 505 { 506 TRACER.debugCaught(DebugLogLevel.ERROR, coe); 507 } 508 509 setResultCode(ResultCode.CANCELED); 510 cancelResult = new CancelResult(ResultCode.CANCELED, null); 511 512 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 513 } 514 finally 515 { 516 // Stop the processing timer. 517 setProcessingStopTime(); 518 519 if(cancelRequest == null || cancelResult == null || 520 cancelResult.getResultCode() != ResultCode.CANCELED || 521 cancelRequest.notifyOriginalRequestor() || 522 DirectoryServer.notifyAbandonedOperations()) 523 { 524 clientConnection.sendResponse(this); 525 } 526 527 // Log the modify response. 528 logModifyResponse(this); 529 530 // Notifies any persistent searches that might be registered with the 531 // server. 532 notifyPersistentSearches(workflowExecuted); 533 534 // Invoke the post-response add plugins. 535 invokePostResponsePlugins(workflowExecuted); 536 537 // If no cancel result, set it 538 if(cancelResult == null) 539 { 540 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 541 } 542 } 543 } 544 545 546 /** 547 * Invokes the post response plugins. If a workflow has been executed 548 * then invoke the post response plugins provided by the workflow 549 * elements of the worklfow, otherwise invoke the post reponse plugins 550 * that have been registered with the current operation. 551 * 552 * @param workflowExecuted <code>true</code> if a workflow has been 553 * executed 554 */ 555 private void invokePostResponsePlugins(boolean workflowExecuted) 556 { 557 // Get the plugin config manager that will be used for invoking plugins. 558 PluginConfigManager pluginConfigManager = 559 DirectoryServer.getPluginConfigManager(); 560 561 // Invoke the post response plugins 562 if (workflowExecuted) 563 { 564 // Invoke the post response plugins that have been registered by 565 // the workflow elements 566 List localOperations = 567 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 568 569 if (localOperations != null) 570 { 571 for (Object localOp : localOperations) 572 { 573 LocalBackendModifyOperation localOperation = 574 (LocalBackendModifyOperation)localOp; 575 pluginConfigManager.invokePostResponseModifyPlugins(localOperation); 576 } 577 } 578 } 579 else 580 { 581 // Invoke the post response plugins that have been registered with 582 // the current operation 583 pluginConfigManager.invokePostResponseModifyPlugins(this); 584 } 585 } 586 587 588 /** 589 * Notifies any persistent searches that might be registered with the server. 590 * If no workflow has been executed then don't notify persistent searches. 591 * 592 * @param workflowExecuted <code>true</code> if a workflow has been 593 * executed 594 */ 595 private void notifyPersistentSearches(boolean workflowExecuted) 596 { 597 if (! workflowExecuted) 598 { 599 return; 600 } 601 602 List localOperations = 603 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 604 605 if (localOperations != null) 606 { 607 for (Object localOp : localOperations) 608 { 609 LocalBackendModifyOperation localOperation = 610 (LocalBackendModifyOperation)localOp; 611 // Notify any persistent searches that might be registered with 612 // the server. 613 if (localOperation.getResultCode() == ResultCode.SUCCESS) 614 { 615 for (PersistentSearch persistentSearch : 616 DirectoryServer.getPersistentSearches()) 617 { 618 try 619 { 620 persistentSearch.processModify(localOperation, 621 localOperation.getCurrentEntry(), 622 localOperation.getModifiedEntry()); 623 } 624 catch (Exception e) 625 { 626 if (debugEnabled()) 627 { 628 TRACER.debugCaught(DebugLogLevel.ERROR, e); 629 } 630 631 Message message = ERR_MODIFY_ERROR_NOTIFYING_PERSISTENT_SEARCH. 632 get(String.valueOf(persistentSearch), getExceptionMessage(e)); 633 logError(message); 634 635 DirectoryServer.deregisterPersistentSearch(persistentSearch); 636 } 637 } 638 } 639 } 640 } 641 } 642 643 644 /** 645 * Updates the error message and the result code of the operation. 646 * 647 * This method is called because no workflows were found to process 648 * the operation. 649 */ 650 private void updateOperationErrMsgAndResCode() 651 { 652 setResultCode(ResultCode.NO_SUCH_OBJECT); 653 appendErrorMessage( 654 ERR_MODIFY_NO_SUCH_ENTRY.get(String.valueOf(getEntryDN()))); 655 } 656 657 658 /** 659 * {@inheritDoc} 660 * 661 * This method always returns null. 662 */ 663 public Entry getCurrentEntry() { 664 // TODO Auto-generated method stub 665 return null; 666 } 667 668 /** 669 * {@inheritDoc} 670 * 671 * This method always returns null. 672 */ 673 public List<AttributeValue> getCurrentPasswords() 674 { 675 return null; 676 } 677 678 /** 679 * {@inheritDoc} 680 * 681 * This method always returns null. 682 */ 683 public Entry getModifiedEntry() 684 { 685 return null; 686 } 687 688 /** 689 * {@inheritDoc} 690 * 691 * This method always returns null. 692 */ 693 public List<AttributeValue> getNewPasswords() 694 { 695 return null; 696 } 697 698 } 699