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