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    import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE;
032    import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ID_TO_ABANDON;
033    import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME;
034    import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE;
035    import static org.opends.server.loggers.AccessLogger.logAbandonRequest;
036    import static org.opends.server.loggers.AccessLogger.logAbandonResult;
037    import static org.opends.messages.CoreMessages.*;
038    import java.util.List;
039    
040    import org.opends.server.api.ClientConnection;
041    import org.opends.server.api.plugin.PluginResult;
042    import org.opends.server.loggers.debug.DebugLogger;
043    import org.opends.server.loggers.debug.DebugTracer;
044    import org.opends.server.types.*;
045    import org.opends.server.types.operation.PostOperationAbandonOperation;
046    import org.opends.server.types.operation.PreParseAbandonOperation;
047    
048    
049    /**
050     * This class defines an operation that may be used to abandon an operation
051     * that may already be in progress in the Directory Server.
052     */
053    public class AbandonOperationBasis extends AbstractOperation
054        implements Runnable,
055                   AbandonOperation,
056                   PreParseAbandonOperation,
057                   PostOperationAbandonOperation
058    {
059    
060      /**
061       * The tracer object for the debug logger.
062       */
063      private static final DebugTracer TRACER = DebugLogger.getTracer();
064    
065      // The message ID of the operation that should be abandoned.
066      private final int idToAbandon;
067    
068    
069      /**
070       * Creates a new abandon operation with the provided information.
071       *
072       * @param  clientConnection  The client connection with which this operation
073       *                           is associated.
074       * @param  operationID       The operation ID for this operation.
075       * @param  messageID         The message ID of the request with which this
076       *                           operation is associated.
077       * @param  requestControls   The set of controls included in the request.
078       * @param  idToAbandon       The message ID of the operation that should be
079       *                           abandoned.
080       */
081      public AbandonOperationBasis(
082          ClientConnection clientConnection,
083          long operationID,
084          int messageID,
085          List<Control> requestControls,
086          int idToAbandon)
087      {
088        super(clientConnection, operationID, messageID, requestControls);
089    
090    
091        this.idToAbandon = idToAbandon;
092        this.cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL,
093            ERR_CANNOT_CANCEL_ABANDON.get());
094      }
095    
096    
097    
098      /**
099       * Retrieves the message ID of the operation that should be abandoned.
100       *
101       * @return  The message ID of the operation that should be abandoned.
102       */
103      public final int getIDToAbandon()
104      {
105        return idToAbandon;
106      }
107    
108    
109    
110      /**
111       * {@inheritDoc}
112       */
113      @Override()
114      public final OperationType getOperationType()
115      {
116        // Note that no debugging will be done in this method because it is a likely
117        // candidate for being called by the logging subsystem.
118    
119        return OperationType.ABANDON;
120      }
121    
122    
123    
124      /**
125       * {@inheritDoc}
126       */
127      @Override()
128      public final String[][] getRequestLogElements()
129      {
130        // Note that no debugging will be done in this method because it is a likely
131        // candidate for being called by the logging subsystem.
132    
133        return new String[][]
134        {
135          new String[] { LOG_ELEMENT_ID_TO_ABANDON, String.valueOf(idToAbandon) }
136        };
137      }
138    
139    
140    
141      /**
142       * {@inheritDoc}
143       */
144      @Override()
145      public final String[][] getResponseLogElements()
146      {
147        // Note that no debugging will be done in this method because it is a likely
148        // candidate for being called by the logging subsystem.
149    
150        // There is no response for an abandon.  However, we will still want to log
151        // information about whether it was successful.
152        String resultCode = String.valueOf(getResultCode().getIntValue());
153    
154        String errorMessage;
155        MessageBuilder errorMessageBuffer = getErrorMessage();
156        if (errorMessageBuffer == null)
157        {
158          errorMessage = null;
159        }
160        else
161        {
162          errorMessage = errorMessageBuffer.toString();
163        }
164    
165        String processingTime =
166             String.valueOf(getProcessingTime());
167    
168        return new String[][]
169        {
170          new String[] { LOG_ELEMENT_RESULT_CODE, resultCode },
171          new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage },
172          new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime }
173        };
174      }
175    
176    
177    
178      /**
179       * {@inheritDoc}
180       */
181      @Override()
182      public final List<Control> getResponseControls()
183      {
184        // An abandon operation can never have a response, so just return an empty
185        // list.
186        return NO_RESPONSE_CONTROLS;
187      }
188    
189    
190    
191      /**
192       * {@inheritDoc}
193       */
194      @Override()
195      public final void addResponseControl(Control control)
196      {
197        // An abandon operation can never have a response, so just ignore this.
198      }
199    
200    
201    
202      /**
203       * {@inheritDoc}
204       */
205      @Override()
206      public final void removeResponseControl(Control control)
207      {
208        // An abandon operation can never have a response, so just ignore this.
209      }
210    
211    
212    
213      /**
214       * Performs the work of actually processing this operation.  This
215       * should include all processing for the operation, including
216       * invoking plugins, logging messages, performing access control,
217       * managing synchronization, and any other work that might need to
218       * be done in the course of processing.
219       */
220      public final void run()
221      {
222        setResultCode(ResultCode.UNDEFINED);
223    
224        // Start the processing timer.
225        setProcessingStartTime();
226    
227        // Log the abandon request message.
228        logAbandonRequest(this);
229    
230        // Get the plugin config manager that will be used for invoking plugins.
231        PluginConfigManager pluginConfigManager =
232             DirectoryServer.getPluginConfigManager();
233    
234        // Create a labeled block of code that we can break out of if a problem is
235        // detected.
236    abandonProcessing:
237        {
238          // Invoke the pre-parse abandon plugins.
239          PluginResult.PreParse preParseResult =
240               pluginConfigManager.invokePreParseAbandonPlugins(this);
241          if (!preParseResult.continueProcessing())
242          {
243            setResultCode(preParseResult.getResultCode());
244            appendErrorMessage(preParseResult.getErrorMessage());
245            setMatchedDN(preParseResult.getMatchedDN());
246            setReferralURLs(preParseResult.getReferralURLs());
247            break abandonProcessing;
248          }
249    
250          // Actually perform the abandon operation.  Make sure to set the result
251          // code to reflect whether the abandon was successful and an error message
252          // if it was not.  Even though there is no response, the result should
253          // still be logged.
254          AbstractOperation operation =
255               clientConnection.getOperationInProgress(idToAbandon);
256          if (operation == null)
257          {
258            setResultCode(ResultCode.NO_SUCH_OPERATION);
259            appendErrorMessage(ERR_ABANDON_OP_NO_SUCH_OPERATION.get(idToAbandon));
260          }
261          else
262          {
263            // Even though it is technically illegal to send a response for
264            // operations that have been abandoned, it may be a good idea to do so
265            // to ensure that the requestor isn't left hanging.  This will be a
266            // configurable option in the server.
267            boolean notifyRequestor = DirectoryServer.notifyAbandonedOperations();
268            Message cancelReason = INFO_CANCELED_BY_ABANDON_REQUEST.get(messageID);
269            CancelResult result =
270                 operation.cancel(new CancelRequest(notifyRequestor, cancelReason));
271            setResultCode(result.getResultCode());
272            appendErrorMessage(result.getResponseMessage());
273          }
274    
275          PluginResult.PostOperation postOpResult =
276              pluginConfigManager.invokePostOperationAbandonPlugins(this);
277          if (!postOpResult.continueProcessing())
278          {
279            setResultCode(preParseResult.getResultCode());
280            appendErrorMessage(preParseResult.getErrorMessage());
281            setMatchedDN(preParseResult.getMatchedDN());
282            setReferralURLs(preParseResult.getReferralURLs());
283            break abandonProcessing;
284          }
285        }
286    
287    
288        // Stop the processing timer.
289        setProcessingStopTime();
290    
291    
292        // Log the result of the abandon operation.
293        logAbandonResult(this);
294      }
295    
296    
297    
298      /**
299       * {@inheritDoc}
300       */
301      @Override()
302      public final void toString(StringBuilder buffer)
303      {
304        buffer.append("AbandonOperation(connID=");
305        buffer.append(clientConnection.getConnectionID());
306        buffer.append(", opID=");
307        buffer.append(operationID);
308        buffer.append(", idToAbandon=");
309        buffer.append(idToAbandon);
310        buffer.append(")");
311      }
312    }