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 */
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 * 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)
112 public 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 }