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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2007-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.core;
028
029import java.util.ArrayList;
030import java.util.List;
031
032import org.forgerock.i18n.slf4j.LocalizedLogger;
033import org.forgerock.opendj.ldap.ByteString;
034import org.forgerock.opendj.ldap.ResultCode;
035import org.opends.server.api.ClientConnection;
036import org.opends.server.types.*;
037import org.opends.server.types.operation.PostResponseDeleteOperation;
038import org.opends.server.types.operation.PreParseDeleteOperation;
039import org.opends.server.workflowelement.localbackend.LocalBackendDeleteOperation;
040
041import static org.opends.messages.CoreMessages.*;
042import static org.opends.server.core.DirectoryServer.*;
043import static org.opends.server.loggers.AccessLogger.*;
044import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
045
046/**
047 * This class defines an operation that may be used to remove an entry from the
048 * Directory Server.
049 */
050public class DeleteOperationBasis
051       extends AbstractOperation
052       implements PreParseDeleteOperation,
053                  DeleteOperation,
054                  PostResponseDeleteOperation
055{
056  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
057
058  /** The raw, unprocessed entry DN as included in the client request. */
059  private ByteString rawEntryDN;
060  /** The DN of the entry for the delete operation. */
061  private DN entryDN;
062  /** The proxied authorization target DN for this operation. */
063  private DN proxiedAuthorizationDN;
064  /** The set of response controls for this delete operation. */
065  private final List<Control> responseControls = new ArrayList<>();
066
067  /**
068   * Creates a new delete operation with the provided information.
069   *
070   * @param  clientConnection  The client connection with which this operation
071   *                           is associated.
072   * @param  operationID       The operation ID for this operation.
073   * @param  messageID         The message ID of the request with which this
074   *                           operation is associated.
075   * @param  requestControls   The set of controls included in the request.
076   * @param  rawEntryDN        The raw, unprocessed DN of the entry to delete,
077   *                           as included in the client request.
078   */
079  public DeleteOperationBasis(ClientConnection clientConnection,
080                         long operationID,
081                         int messageID, List<Control> requestControls,
082                         ByteString rawEntryDN)
083  {
084    super(clientConnection, operationID, messageID, requestControls);
085
086    this.rawEntryDN = rawEntryDN;
087  }
088
089
090
091  /**
092   * Creates a new delete operation with the provided information.
093   *
094   * @param  clientConnection  The client connection with which this operation
095   *                           is associated.
096   * @param  operationID       The operation ID for this operation.
097   * @param  messageID         The message ID of the request with which this
098   *                           operation is associated.
099   * @param  requestControls   The set of controls included in the request.
100   * @param  entryDN           The entry DN for this delete operation.
101   */
102  public DeleteOperationBasis(ClientConnection clientConnection,
103                         long operationID,
104                         int messageID, List<Control> requestControls,
105                         DN entryDN)
106  {
107    super(clientConnection, operationID, messageID, requestControls);
108
109    this.entryDN = entryDN;
110    rawEntryDN = ByteString.valueOfUtf8(entryDN.toString());
111  }
112
113  @Override
114  public final ByteString getRawEntryDN()
115  {
116    return rawEntryDN;
117  }
118
119  @Override
120  public final void setRawEntryDN(ByteString rawEntryDN)
121  {
122    this.rawEntryDN = rawEntryDN;
123
124    entryDN = null;
125  }
126
127  @Override
128  public final DN getEntryDN()
129  {
130    try
131    {
132      if (entryDN == null)
133      {
134        entryDN = DN.decode(rawEntryDN);
135      }
136    }
137    catch (DirectoryException de)
138    {
139      logger.traceException(de);
140      setResponseData(de);
141    }
142    return entryDN;
143  }
144
145  @Override
146  public final OperationType getOperationType()
147  {
148    // Note that no debugging will be done in this method because it is a likely
149    // candidate for being called by the logging subsystem.
150    return OperationType.DELETE;
151  }
152
153  @Override
154  public DN getProxiedAuthorizationDN()
155  {
156    return proxiedAuthorizationDN;
157  }
158
159  @Override
160  public final List<Control> getResponseControls()
161  {
162    return responseControls;
163  }
164
165  @Override
166  public final void addResponseControl(Control control)
167  {
168    responseControls.add(control);
169  }
170
171  @Override
172  public final void removeResponseControl(Control control)
173  {
174    responseControls.remove(control);
175  }
176
177  @Override
178  public final void toString(StringBuilder buffer)
179  {
180    buffer.append("DeleteOperation(connID=");
181    buffer.append(clientConnection.getConnectionID());
182    buffer.append(", opID=");
183    buffer.append(operationID);
184    buffer.append(", dn=");
185    buffer.append(rawEntryDN);
186    buffer.append(")");
187  }
188
189  @Override
190  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
191  {
192    this.proxiedAuthorizationDN = proxiedAuthorizationDN;
193  }
194
195  @Override
196  public final void run()
197  {
198    setResultCode(ResultCode.UNDEFINED);
199
200    // Start the processing timer.
201    setProcessingStartTime();
202
203    logDeleteRequest(this);
204
205    // This flag is set to true as soon as a workflow has been executed.
206    boolean workflowExecuted = false;
207    try
208    {
209      // Invoke the pre-parse delete plugins.
210      if (!processOperationResult(getPluginConfigManager().invokePreParseDeletePlugins(this)))
211      {
212        return;
213      }
214
215
216      // Check for a request to cancel this operation.
217      checkIfCanceled(false);
218
219
220      // Process the entry DN to convert it from its raw form as provided by the
221      // client to the form required for the rest of the delete processing.
222      DN entryDN = getEntryDN();
223      if (entryDN == null){
224        return;
225      }
226
227      workflowExecuted = execute(this, entryDN);
228    }
229    catch(CanceledOperationException coe)
230    {
231      logger.traceException(coe);
232
233      setResultCode(ResultCode.CANCELLED);
234      cancelResult = new CancelResult(ResultCode.CANCELLED, null);
235
236      appendErrorMessage(coe.getCancelRequest().getCancelReason());
237    }
238    finally
239    {
240      // Stop the processing timer.
241      setProcessingStopTime();
242
243      // Log the delete response.
244      logDeleteResponse(this);
245
246      if(cancelRequest == null || cancelResult == null ||
247          cancelResult.getResultCode() != ResultCode.CANCELLED ||
248          cancelRequest.notifyOriginalRequestor() ||
249          DirectoryServer.notifyAbandonedOperations())
250      {
251        clientConnection.sendResponse(this);
252      }
253
254
255      // Invoke the post-response callbacks.
256      if (workflowExecuted) {
257        invokePostResponseCallbacks();
258      }
259
260      // Invoke the post-response delete plugins.
261      invokePostResponsePlugins(workflowExecuted);
262
263      // If no cancel result, set it
264      if(cancelResult == null)
265      {
266        cancelResult = new CancelResult(ResultCode.TOO_LATE, null);
267      }
268    }
269  }
270
271
272  /**
273   * Invokes the post response plugins. If a workflow has been executed
274   * then invoke the post response plugins provided by the workflow
275   * elements of the workflow, otherwise invoke the post response plugins
276   * that have been registered with the current operation.
277   *
278   * @param workflowExecuted <code>true</code> if a workflow has been executed
279   */
280  private void invokePostResponsePlugins(boolean workflowExecuted)
281  {
282    // Invoke the post response plugins
283    if (workflowExecuted)
284    {
285      // Invoke the post response plugins that have been registered by
286      // the workflow elements
287      List<LocalBackendDeleteOperation> localOperations =
288        (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
289
290      if (localOperations != null)
291      {
292        for (LocalBackendDeleteOperation localOperation : localOperations)
293        {
294          getPluginConfigManager().invokePostResponseDeletePlugins(localOperation);
295        }
296      }
297    }
298    else
299    {
300      // Invoke the post response plugins that have been registered with
301      // the current operation
302      getPluginConfigManager().invokePostResponseDeletePlugins(this);
303    }
304  }
305
306  @Override
307  public void updateOperationErrMsgAndResCode()
308  {
309    setResultCode(ResultCode.NO_SUCH_OBJECT);
310    appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(getEntryDN()));
311  }
312
313  /**
314   * {@inheritDoc}
315   *
316   * This method always returns null.
317   */
318  @Override
319  public Entry getEntryToDelete() {
320    return null;
321  }
322}