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