001/* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.extensions; 022 023 024 025import com.unboundid.asn1.ASN1Element; 026import com.unboundid.asn1.ASN1Integer; 027import com.unboundid.asn1.ASN1OctetString; 028import com.unboundid.asn1.ASN1Sequence; 029import com.unboundid.ldap.sdk.AsyncRequestID; 030import com.unboundid.ldap.sdk.Control; 031import com.unboundid.ldap.sdk.ExtendedRequest; 032import com.unboundid.ldap.sdk.ExtendedResult; 033import com.unboundid.ldap.sdk.LDAPConnection; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.ResultCode; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*; 041import static com.unboundid.util.Debug.*; 042 043 044 045/** 046 * This class provides an implementation of the LDAP cancel extended request as 047 * defined in <A HREF="http://www.ietf.org/rfc/rfc3909.txt">RFC 3909</A>. It 048 * may be used to request that the server interrupt processing on another 049 * operation in progress on the same connection. It behaves much like the 050 * abandon operation, with the exception that both the cancel request and the 051 * operation that is canceled will receive responses, whereas an abandon request 052 * never returns a response, and the operation that is abandoned will also not 053 * receive a response if the abandon is successful. 054 * <BR><BR> 055 * <H2>Example</H2> 056 * The following example initiates an asynchronous modify operation and then 057 * attempts to cancel it: 058 * <PRE> 059 * Modification mod = new Modification(ModificationType.REPLACE, 060 * "description", "This is the new description."); 061 * ModifyRequest modifyRequest = 062 * new ModifyRequest("dc=example,dc=com", mod); 063 * 064 * AsyncRequestID asyncRequestID = 065 * connection.asyncModify(modifyRequest, myAsyncResultListener); 066 * 067 * // Assume that we've waited a reasonable amount of time but the modify 068 * // hasn't completed yet so we'll try to cancel it. 069 * 070 * ExtendedResult cancelResult; 071 * try 072 * { 073 * cancelResult = connection.processExtendedOperation( 074 * new CancelExtendedRequest(asyncRequestID)); 075 * // This doesn't necessarily mean that the operation was successful, since 076 * // some kinds of extended operations (like cancel) return non-success 077 * // results under normal conditions. 078 * } 079 * catch (LDAPException le) 080 * { 081 * // For an extended operation, this generally means that a problem was 082 * // encountered while trying to send the request or read the result. 083 * cancelResult = new ExtendedResult(le); 084 * } 085 * 086 * switch (cancelResult.getResultCode().intValue()) 087 * { 088 * case ResultCode.CANCELED_INT_VALUE: 089 * // The modify operation was successfully canceled. 090 * break; 091 * case ResultCode.CANNOT_CANCEL_INT_VALUE: 092 * // This indicates that the server isn't capable of canceling that 093 * // type of operation. This probably won't happen for this kind of 094 * // modify operation, but it could happen for other kinds of operations. 095 * break; 096 * case ResultCode.TOO_LATE_INT_VALUE: 097 * // This indicates that the cancel request was received too late and the 098 * // server is intending to process the operation. 099 * break; 100 * case ResultCode.NO_SUCH_OPERATION_INT_VALUE: 101 * // This indicates that the server doesn't know anything about the 102 * // operation, most likely because it has already completed. 103 * break; 104 * default: 105 * // This suggests that the operation failed for some other reason. 106 * break; 107 * } 108 * </PRE> 109 */ 110@NotMutable() 111@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 112public final class CancelExtendedRequest 113 extends ExtendedRequest 114{ 115 /** 116 * The OID (1.3.6.1.1.8) for the cancel extended request. 117 */ 118 public static final String CANCEL_REQUEST_OID = "1.3.6.1.1.8"; 119 120 121 122 /** 123 * The serial version UID for this serializable class. 124 */ 125 private static final long serialVersionUID = -7170687636394194183L; 126 127 128 129 // The message ID of the request to cancel. 130 private final int targetMessageID; 131 132 133 134 /** 135 * Creates a new cancel extended request that will cancel the request with the 136 * specified async request ID. 137 * 138 * @param requestID The async request ID of the request to cancel. It must 139 * not be {@code null}. 140 */ 141 public CancelExtendedRequest(final AsyncRequestID requestID) 142 { 143 this(requestID.getMessageID(), null); 144 } 145 146 147 148 /** 149 * Creates a new cancel extended request that will cancel the request with the 150 * specified message ID. 151 * 152 * @param targetMessageID The message ID of the request to cancel. 153 */ 154 public CancelExtendedRequest(final int targetMessageID) 155 { 156 this(targetMessageID, null); 157 } 158 159 160 161 /** 162 * Creates a new cancel extended request that will cancel the request with the 163 * specified request ID. 164 * 165 * @param requestID The async request ID of the request to cancel. It must 166 * not be {@code null}. 167 * @param controls The set of controls to include in the request. 168 */ 169 public CancelExtendedRequest(final AsyncRequestID requestID, 170 final Control[] controls) 171 { 172 this(requestID.getMessageID(), controls); 173 } 174 175 176 177 /** 178 * Creates a new cancel extended request that will cancel the request with the 179 * specified message ID. 180 * 181 * @param targetMessageID The message ID of the request to cancel. 182 * @param controls The set of controls to include in the request. 183 */ 184 public CancelExtendedRequest(final int targetMessageID, 185 final Control[] controls) 186 { 187 super(CANCEL_REQUEST_OID, encodeValue(targetMessageID), controls); 188 189 this.targetMessageID = targetMessageID; 190 } 191 192 193 194 /** 195 * Creates a new cancel extended request from the provided generic extended 196 * request. 197 * 198 * @param extendedRequest The generic extended request to use to create this 199 * cancel extended request. 200 * 201 * @throws LDAPException If a problem occurs while decoding the request. 202 */ 203 public CancelExtendedRequest(final ExtendedRequest extendedRequest) 204 throws LDAPException 205 { 206 super(extendedRequest); 207 208 final ASN1OctetString value = extendedRequest.getValue(); 209 if (value == null) 210 { 211 throw new LDAPException(ResultCode.DECODING_ERROR, 212 ERR_CANCEL_REQUEST_NO_VALUE.get()); 213 } 214 215 try 216 { 217 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 218 final ASN1Element[] elements = 219 ASN1Sequence.decodeAsSequence(valueElement).elements(); 220 targetMessageID = ASN1Integer.decodeAsInteger(elements[0]).intValue(); 221 } 222 catch (Exception e) 223 { 224 debugException(e); 225 throw new LDAPException(ResultCode.DECODING_ERROR, 226 ERR_CANCEL_REQUEST_CANNOT_DECODE.get(e), e); 227 } 228 } 229 230 231 232 /** 233 * Generates a properly-encoded request value for this cancel extended 234 * request. 235 * 236 * @param targetMessageID The message ID of the request to cancel. 237 * 238 * @return An ASN.1 octet string containing the encoded request value. 239 */ 240 private static ASN1OctetString encodeValue(final int targetMessageID) 241 { 242 final ASN1Element[] sequenceValues = 243 { 244 new ASN1Integer(targetMessageID) 245 }; 246 247 return new ASN1OctetString(new ASN1Sequence(sequenceValues).encode()); 248 } 249 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override() 256 protected ExtendedResult process(final LDAPConnection connection, 257 final int depth) 258 throws LDAPException 259 { 260 if (connection.synchronousMode()) 261 { 262 throw new LDAPException(ResultCode.NOT_SUPPORTED, 263 ERR_CANCEL_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 264 } 265 266 return super.process(connection, depth); 267 } 268 269 270 271 /** 272 * Retrieves the message ID of the request to cancel. 273 * 274 * @return The message ID of the request to cancel. 275 */ 276 public int getTargetMessageID() 277 { 278 return targetMessageID; 279 } 280 281 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override() 287 public CancelExtendedRequest duplicate() 288 { 289 return duplicate(getControls()); 290 } 291 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 @Override() 298 public CancelExtendedRequest duplicate(final Control[] controls) 299 { 300 final CancelExtendedRequest cancelRequest = 301 new CancelExtendedRequest(targetMessageID, controls); 302 cancelRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 303 return cancelRequest; 304 } 305 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override() 312 public String getExtendedRequestName() 313 { 314 return INFO_EXTENDED_REQUEST_NAME_CANCEL.get(); 315 } 316 317 318 319 /** 320 * {@inheritDoc} 321 */ 322 @Override() 323 public void toString(final StringBuilder buffer) 324 { 325 buffer.append("CancelExtendedRequest(targetMessageID="); 326 buffer.append(targetMessageID); 327 328 final Control[] controls = getControls(); 329 if (controls.length > 0) 330 { 331 buffer.append(", controls={"); 332 for (int i=0; i < controls.length; i++) 333 { 334 if (i > 0) 335 { 336 buffer.append(", "); 337 } 338 339 buffer.append(controls[i]); 340 } 341 buffer.append('}'); 342 } 343 344 buffer.append(')'); 345 } 346}