001 /*
002 * Copyright 2007-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 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 import com.unboundid.util.NotMutable;
037 import com.unboundid.util.ThreadSafety;
038 import com.unboundid.util.ThreadSafetyLevel;
039
040 import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
041 import 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 * CancelExtendedRequest cancelRequest =
071 * new CancelExtendedRequest(asyncRequestID);
072 *
073 * // NOTE: The processExtendedOperation method will only throw an exception
074 * // if a problem occurs while trying to send the request or read the
075 * // response. It will not throw an exception because of a non-success
076 * // response. That's good for us in this case because the cancel result
077 * // should never be "SUCCESS".
078 * ExtendedResult cancelResult =
079 * connection.processExtendedOperation(cancelRequest);
080 * switch (cancelResult.getResultCode())
081 * {
082 * case ResultCode.CANCELED:
083 * System.out.println("The operation was successfully canceled.");
084 * break;
085 * case ResultCode.NO_SUCH_OPERATION:
086 * System.out.println("The server didn't know anything about the " +
087 * "operation. Maybe it's already completed.");
088 * break;
089 * case ResultCode.TOO_LATE:
090 * System.out.println("It was too late in the operation processing " +
091 * "to cancel the operation.");
092 * break;
093 * case ResultCode.CANNOT_CANCEL:
094 * System.out.println("The target operation is not one that could be " +
095 * "canceled.");
096 * break;
097 * default:
098 * System.err.println("An error occurred while processing the cancel " +
099 * "request.");
100 * break;
101 * }
102 * </PRE>
103 */
104 @NotMutable()
105 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
106 public final class CancelExtendedRequest
107 extends ExtendedRequest
108 {
109 /**
110 * The OID (1.3.6.1.1.8) for the cancel extended request.
111 */
112 public static final String CANCEL_REQUEST_OID = "1.3.6.1.1.8";
113
114
115
116 /**
117 * The serial version UID for this serializable class.
118 */
119 private static final long serialVersionUID = -7170687636394194183L;
120
121
122
123 // The message ID of the request to cancel.
124 private final int targetMessageID;
125
126
127
128 /**
129 * Creates a new cancel extended request that will cancel the request with the
130 * specified async request ID.
131 *
132 * @param requestID The async request ID of the request to cancel. It must
133 * not be {@code null}.
134 */
135 public CancelExtendedRequest(final AsyncRequestID requestID)
136 {
137 this(requestID.getMessageID(), null);
138 }
139
140
141
142 /**
143 * Creates a new cancel extended request that will cancel the request with the
144 * specified message ID.
145 *
146 * @param targetMessageID The message ID of the request to cancel.
147 */
148 public CancelExtendedRequest(final int targetMessageID)
149 {
150 this(targetMessageID, null);
151 }
152
153
154
155 /**
156 * Creates a new cancel extended request that will cancel the request with the
157 * specified request ID.
158 *
159 * @param requestID The async request ID of the request to cancel. It must
160 * not be {@code null}.
161 * @param controls The set of controls to include in the request.
162 */
163 public CancelExtendedRequest(final AsyncRequestID requestID,
164 final Control[] controls)
165 {
166 this(requestID.getMessageID(), controls);
167 }
168
169
170
171 /**
172 * Creates a new cancel extended request that will cancel the request with the
173 * specified message ID.
174 *
175 * @param targetMessageID The message ID of the request to cancel.
176 * @param controls The set of controls to include in the request.
177 */
178 public CancelExtendedRequest(final int targetMessageID,
179 final Control[] controls)
180 {
181 super(CANCEL_REQUEST_OID, encodeValue(targetMessageID), controls);
182
183 this.targetMessageID = targetMessageID;
184 }
185
186
187
188 /**
189 * Creates a new cancel extended request from the provided generic extended
190 * request.
191 *
192 * @param extendedRequest The generic extended request to use to create this
193 * cancel extended request.
194 *
195 * @throws LDAPException If a problem occurs while decoding the request.
196 */
197 public CancelExtendedRequest(final ExtendedRequest extendedRequest)
198 throws LDAPException
199 {
200 super(extendedRequest);
201
202 final ASN1OctetString value = extendedRequest.getValue();
203 if (value == null)
204 {
205 throw new LDAPException(ResultCode.DECODING_ERROR,
206 ERR_CANCEL_REQUEST_NO_VALUE.get());
207 }
208
209 try
210 {
211 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
212 final ASN1Element[] elements =
213 ASN1Sequence.decodeAsSequence(valueElement).elements();
214 targetMessageID = ASN1Integer.decodeAsInteger(elements[0]).intValue();
215 }
216 catch (Exception e)
217 {
218 debugException(e);
219 throw new LDAPException(ResultCode.DECODING_ERROR,
220 ERR_CANCEL_REQUEST_CANNOT_DECODE.get(e), e);
221 }
222 }
223
224
225
226 /**
227 * Generates a properly-encoded request value for this cancel extended
228 * request.
229 *
230 * @param targetMessageID The message ID of the request to cancel.
231 *
232 * @return An ASN.1 octet string containing the encoded request value.
233 */
234 private static ASN1OctetString encodeValue(final int targetMessageID)
235 {
236 final ASN1Element[] sequenceValues =
237 {
238 new ASN1Integer(targetMessageID)
239 };
240
241 return new ASN1OctetString(new ASN1Sequence(sequenceValues).encode());
242 }
243
244
245
246 /**
247 * {@inheritDoc}
248 */
249 @Override()
250 protected ExtendedResult process(final LDAPConnection connection,
251 final int depth)
252 throws LDAPException
253 {
254 if (connection.synchronousMode())
255 {
256 throw new LDAPException(ResultCode.NOT_SUPPORTED,
257 ERR_CANCEL_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
258 }
259
260 return super.process(connection, depth);
261 }
262
263
264
265 /**
266 * Retrieves the message ID of the request to cancel.
267 *
268 * @return The message ID of the request to cancel.
269 */
270 public int getTargetMessageID()
271 {
272 return targetMessageID;
273 }
274
275
276
277 /**
278 * {@inheritDoc}
279 */
280 @Override()
281 public CancelExtendedRequest duplicate()
282 {
283 return duplicate(getControls());
284 }
285
286
287
288 /**
289 * {@inheritDoc}
290 */
291 @Override()
292 public CancelExtendedRequest duplicate(final Control[] controls)
293 {
294 final CancelExtendedRequest cancelRequest =
295 new CancelExtendedRequest(targetMessageID, controls);
296 cancelRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
297 return cancelRequest;
298 }
299
300
301
302 /**
303 * {@inheritDoc}
304 */
305 @Override()
306 public String getExtendedRequestName()
307 {
308 return INFO_EXTENDED_REQUEST_NAME_CANCEL.get();
309 }
310
311
312
313 /**
314 * {@inheritDoc}
315 */
316 @Override()
317 public void toString(final StringBuilder buffer)
318 {
319 buffer.append("CancelExtendedRequest(targetMessageID=");
320 buffer.append(targetMessageID);
321
322 final Control[] controls = getControls();
323 if (controls.length > 0)
324 {
325 buffer.append(", controls={");
326 for (int i=0; i < controls.length; i++)
327 {
328 if (i > 0)
329 {
330 buffer.append(", ");
331 }
332
333 buffer.append(controls[i]);
334 }
335 buffer.append('}');
336 }
337
338 buffer.append(')');
339 }
340 }