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 }