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