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 }