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