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