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
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 }