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-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.core;
028
029import static org.opends.messages.CoreMessages.*;
030import static org.opends.server.core.DirectoryServer.*;
031import static org.opends.server.loggers.AccessLogger.*;
032
033import java.util.List;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.forgerock.opendj.ldap.ResultCode;
037import org.opends.server.api.ClientConnection;
038import org.opends.server.types.*;
039import org.opends.server.types.operation.PostOperationAbandonOperation;
040import org.opends.server.types.operation.PreParseAbandonOperation;
041
042/**
043 * This class defines an operation that may be used to abandon an operation
044 * that may already be in progress in the Directory Server.
045 */
046public class AbandonOperationBasis extends AbstractOperation
047    implements AbandonOperation,
048               PreParseAbandonOperation,
049               PostOperationAbandonOperation
050{
051
052  /** The message ID of the operation that should be abandoned. */
053  private final int idToAbandon;
054
055
056  /**
057   * Creates a new abandon operation with the provided information.
058   *
059   * @param  clientConnection  The client connection with which this operation
060   *                           is associated.
061   * @param  operationID       The operation ID for this operation.
062   * @param  messageID         The message ID of the request with which this
063   *                           operation is associated.
064   * @param  requestControls   The set of controls included in the request.
065   * @param  idToAbandon       The message ID of the operation that should be
066   *                           abandoned.
067   */
068  public AbandonOperationBasis(
069      ClientConnection clientConnection,
070      long operationID,
071      int messageID,
072      List<Control> requestControls,
073      int idToAbandon)
074  {
075    super(clientConnection, operationID, messageID, requestControls);
076
077
078    this.idToAbandon = idToAbandon;
079    this.cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL,
080        ERR_CANNOT_CANCEL_ABANDON.get());
081  }
082
083
084
085  /**
086   * Retrieves the message ID of the operation that should be abandoned.
087   *
088   * @return  The message ID of the operation that should be abandoned.
089   */
090  @Override
091  public final int getIDToAbandon()
092  {
093    return idToAbandon;
094  }
095
096
097
098  /** {@inheritDoc} */
099  @Override
100  public DN getProxiedAuthorizationDN()
101  {
102    return null;
103  }
104
105
106
107  /** {@inheritDoc} */
108  @Override
109  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
110  {
111  }
112
113
114
115  /** {@inheritDoc} */
116  @Override
117  public final OperationType getOperationType()
118  {
119    // Note that no debugging will be done in this method because it is a likely
120    // candidate for being called by the logging subsystem.
121
122    return OperationType.ABANDON;
123  }
124
125
126
127  /** {@inheritDoc} */
128  @Override
129  public final List<Control> getResponseControls()
130  {
131    // An abandon operation can never have a response, so just return an empty
132    // list.
133    return NO_RESPONSE_CONTROLS;
134  }
135
136
137
138  /** {@inheritDoc} */
139  @Override
140  public final void addResponseControl(Control control)
141  {
142    // An abandon operation can never have a response, so just ignore this.
143  }
144
145
146
147  /** {@inheritDoc} */
148  @Override
149  public final void removeResponseControl(Control control)
150  {
151    // An abandon operation can never have a response, so just ignore this.
152  }
153
154
155
156  /**
157   * Performs the work of actually processing this operation.  This
158   * should include all processing for the operation, including
159   * invoking plugins, logging messages, performing access control,
160   * managing synchronization, and any other work that might need to
161   * be done in the course of processing.
162   */
163  @Override
164  public final void run()
165  {
166    setResultCode(ResultCode.UNDEFINED);
167
168    // Start the processing timer.
169    setProcessingStartTime();
170
171    logAbandonRequest(this);
172
173    // Create a labeled block of code that we can break out of if a problem is detected.
174abandonProcessing:
175    {
176      // Invoke the pre-parse abandon plugins.
177      if (!processOperationResult(getPluginConfigManager().invokePreParseAbandonPlugins(this)))
178      {
179        break abandonProcessing;
180      }
181
182      // Actually perform the abandon operation.  Make sure to set the result
183      // code to reflect whether the abandon was successful and an error message
184      // if it was not.  Even though there is no response, the result should
185      // still be logged.
186      //
187      // Even though it is technically illegal to send a response for
188      // operations that have been abandoned, it may be a good idea to do so
189      // to ensure that the requestor isn't left hanging.  This will be a
190      // configurable option in the server.
191      boolean notifyRequestor = DirectoryServer.notifyAbandonedOperations();
192
193      LocalizableMessage cancelReason = INFO_CANCELED_BY_ABANDON_REQUEST.get(messageID);
194
195      CancelRequest _cancelRequest = new CancelRequest(notifyRequestor,
196                                                       cancelReason);
197
198      CancelResult result = clientConnection.cancelOperation(idToAbandon,
199                                                             _cancelRequest);
200
201      setResultCode(result.getResultCode());
202      appendErrorMessage(result.getResponseMessage());
203
204      if (!processOperationResult(getPluginConfigManager().invokePostOperationAbandonPlugins(this)))
205      {
206        break abandonProcessing;
207      }
208    }
209
210
211    // Stop the processing timer.
212    setProcessingStopTime();
213
214
215    // Log the result of the abandon operation.
216    logAbandonResult(this);
217  }
218
219
220
221  /** {@inheritDoc} */
222  @Override
223  public final void toString(StringBuilder buffer)
224  {
225    buffer.append("AbandonOperation(connID=");
226    buffer.append(clientConnection.getConnectionID());
227    buffer.append(", opID=");
228    buffer.append(operationID);
229    buffer.append(", idToAbandon=");
230    buffer.append(idToAbandon);
231    buffer.append(")");
232  }
233}