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.controls;
022
023
024
025 import com.unboundid.asn1.ASN1Element;
026 import com.unboundid.asn1.ASN1Enumerated;
027 import com.unboundid.asn1.ASN1Exception;
028 import com.unboundid.asn1.ASN1Integer;
029 import com.unboundid.asn1.ASN1OctetString;
030 import com.unboundid.asn1.ASN1Sequence;
031 import com.unboundid.ldap.sdk.Control;
032 import com.unboundid.ldap.sdk.DecodeableControl;
033 import com.unboundid.ldap.sdk.LDAPException;
034 import com.unboundid.ldap.sdk.ResultCode;
035 import com.unboundid.ldap.sdk.SearchResult;
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.controls.ControlMessages.*;
041 import static com.unboundid.util.Debug.*;
042
043
044
045 /**
046 * This class provides an implementation of the virtual list view (VLV) response
047 * control, as defined in draft-ietf-ldapext-ldapv3-vlv. It may be used to
048 * provide information about the result of virtual list view processing for a
049 * search containing the {@link VirtualListViewRequestControl}.
050 * <BR><BR>
051 * The virtual list view response control may include the following elements:
052 * <UL>
053 * <LI>{@code resultCode} -- A result code that indicates the result of the
054 * virtual list view processing. It may be the same as or different from
055 * the result code contained in the search result done message.</LI>
056 * <LI>{@code targetPosition} -- The offset of the target entry specified by
057 * the client in the result set.</LI>
058 * <LI>{@code contentCount} -- The estimated total number of entries in the
059 * entire result set.</LI>
060 * <LI>{@code contextID} -- An optional cookie that the client should include
061 * in the next request as part of the virtual list view sequence.</LI>
062 * </UL>
063 */
064 @NotMutable()
065 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066 public final class VirtualListViewResponseControl
067 extends Control
068 implements DecodeableControl
069 {
070 /**
071 * The OID (2.16.840.1.113730.3.4.10) for the virtual list view response
072 * control.
073 */
074 public static final String VIRTUAL_LIST_VIEW_RESPONSE_OID =
075 "2.16.840.1.113730.3.4.10";
076
077
078
079 /**
080 * The serial version UID for this serializable class.
081 */
082 private static final long serialVersionUID = -534656674756287217L;
083
084
085
086 // The context ID for this VLV response control, if available.
087 private final ASN1OctetString contextID;
088
089 // The estimated total number of entries in the result set.
090 private final int contentCount;
091
092 // The result code for this VLV response control.
093 private final ResultCode resultCode;
094
095 // The offset of the target entry for this VLV response control.
096 private final int targetPosition;
097
098
099
100 /**
101 * Creates a new empty control instance that is intended to be used only for
102 * decoding controls via the {@code DecodeableControl} interface.
103 */
104 VirtualListViewResponseControl()
105 {
106 targetPosition = -1;
107 contentCount = -1;
108 resultCode = null;
109 contextID = null;
110 }
111
112
113
114 /**
115 * Creates a new virtual list view response control with the provided
116 * information. It will not be marked critical.
117 *
118 * @param targetPosition The offset of the target entry for this VLV
119 * response control.
120 * @param contentCount The estimated total number of entries in the
121 * result set.
122 * @param resultCode The result code for this VLV response control.
123 * @param contextID The context ID for this VLV response control. It
124 * may be {@code null} if no context ID is available.
125 */
126 public VirtualListViewResponseControl(final int targetPosition,
127 final int contentCount, final ResultCode resultCode,
128 final ASN1OctetString contextID)
129 {
130 super(VIRTUAL_LIST_VIEW_RESPONSE_OID, false,
131 encodeValue(targetPosition, contentCount, resultCode, contextID));
132
133 this.targetPosition = targetPosition;
134 this.contentCount = contentCount;
135 this.resultCode = resultCode;
136 this.contextID = contextID;
137 }
138
139
140
141 /**
142 * Creates a new virtual list view response control from the information
143 * contained in the provided control.
144 *
145 * @param oid The OID for the control.
146 * @param isCritical Indicates whether the control should be marked
147 * critical.
148 * @param value The encoded value for the control. This may be
149 * {@code null} if no value was provided.
150 *
151 * @throws LDAPException If a problem occurs while attempting to decode the
152 * provided control as a virtual list view response
153 * control.
154 */
155 public VirtualListViewResponseControl(final String oid,
156 final boolean isCritical,
157 final ASN1OctetString value)
158 throws LDAPException
159 {
160 super(oid, isCritical, value);
161
162 if (value == null)
163 {
164 throw new LDAPException(ResultCode.DECODING_ERROR,
165 ERR_VLV_RESPONSE_NO_VALUE.get());
166 }
167
168 final ASN1Sequence valueSequence;
169 try
170 {
171 final ASN1Element valueElement =
172 ASN1Element.decode(value.getValue());
173 valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
174 }
175 catch (final ASN1Exception ae)
176 {
177 debugException(ae);
178 throw new LDAPException(ResultCode.DECODING_ERROR,
179 ERR_VLV_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
180 }
181
182 final ASN1Element[] valueElements = valueSequence.elements();
183 if ((valueElements.length < 3) || (valueElements.length > 4))
184 {
185 throw new LDAPException(ResultCode.DECODING_ERROR,
186 ERR_VLV_RESPONSE_INVALID_ELEMENT_COUNT.get(
187 valueElements.length));
188 }
189
190 try
191 {
192 targetPosition = ASN1Integer.decodeAsInteger(valueElements[0]).intValue();
193 }
194 catch (final ASN1Exception ae)
195 {
196 debugException(ae);
197 throw new LDAPException(ResultCode.DECODING_ERROR,
198 ERR_VLV_RESPONSE_FIRST_NOT_INTEGER.get(ae), ae);
199 }
200
201 try
202 {
203 contentCount = ASN1Integer.decodeAsInteger(valueElements[1]).intValue();
204 }
205 catch (final ASN1Exception ae)
206 {
207 debugException(ae);
208 throw new LDAPException(ResultCode.DECODING_ERROR,
209 ERR_VLV_RESPONSE_SECOND_NOT_INTEGER.get(ae), ae);
210 }
211
212 try
213 {
214 final int rc =
215 ASN1Enumerated.decodeAsEnumerated(valueElements[2]).intValue();
216 resultCode = ResultCode.valueOf(rc);
217 }
218 catch (final ASN1Exception ae)
219 {
220 debugException(ae);
221 throw new LDAPException(ResultCode.DECODING_ERROR,
222 ERR_VLV_RESPONSE_THIRD_NOT_ENUM.get(ae), ae);
223 }
224
225 if (valueElements.length == 4)
226 {
227 contextID = ASN1OctetString.decodeAsOctetString(valueElements[3]);
228 }
229 else
230 {
231 contextID = null;
232 }
233 }
234
235
236
237 /**
238 * {@inheritDoc}
239 */
240 public VirtualListViewResponseControl
241 decodeControl(final String oid, final boolean isCritical,
242 final ASN1OctetString value)
243 throws LDAPException
244 {
245 return new VirtualListViewResponseControl(oid, isCritical, value);
246 }
247
248
249
250 /**
251 * Extracts a virtual list view response control from the provided result.
252 *
253 * @param result The result from which to retrieve the virtual list view
254 * response control.
255 *
256 * @return The virtual list view response control contained in the provided
257 * result, or {@code null} if the result did not contain a virtual
258 * list view response control.
259 *
260 * @throws LDAPException If a problem is encountered while attempting to
261 * decode the virtual list view response control
262 * contained in the provided result.
263 */
264 public static VirtualListViewResponseControl get(final SearchResult result)
265 throws LDAPException
266 {
267 final Control c = result.getResponseControl(VIRTUAL_LIST_VIEW_RESPONSE_OID);
268 if (c == null)
269 {
270 return null;
271 }
272
273 if (c instanceof VirtualListViewResponseControl)
274 {
275 return (VirtualListViewResponseControl) c;
276 }
277 else
278 {
279 return new VirtualListViewResponseControl(c.getOID(), c.isCritical(),
280 c.getValue());
281 }
282 }
283
284
285
286 /**
287 * Encodes the provided information into an octet string that can be used as
288 * the value for this control.
289 *
290 * @param targetPosition The offset of the target entry for this VLV
291 * response control.
292 * @param contentCount The estimated total number of entries in the
293 * result set.
294 * @param resultCode The result code for this VLV response control.
295 * @param contextID The context ID for this VLV response control. It
296 * may be {@code null} if no context ID is available.
297 *
298 * @return An ASN.1 octet string that can be used as the value for this
299 * control.
300 */
301 private static ASN1OctetString encodeValue(final int targetPosition,
302 final int contentCount,
303 final ResultCode resultCode,
304 final ASN1OctetString contextID)
305 {
306 final ASN1Element[] vlvElements;
307 if (contextID == null)
308 {
309 vlvElements = new ASN1Element[]
310 {
311 new ASN1Integer(targetPosition),
312 new ASN1Integer(contentCount),
313 new ASN1Enumerated(resultCode.intValue())
314 };
315 }
316 else
317 {
318 vlvElements = new ASN1Element[]
319 {
320 new ASN1Integer(targetPosition),
321 new ASN1Integer(contentCount),
322 new ASN1Enumerated(resultCode.intValue()),
323 contextID
324 };
325 }
326
327 return new ASN1OctetString(new ASN1Sequence(vlvElements).encode());
328 }
329
330
331
332 /**
333 * Retrieves the offset of the target entry for this virtual list view
334 * response control.
335 *
336 * @return The offset of the target entry for this virtual list view response
337 * control.
338 */
339 public int getTargetPosition()
340 {
341 return targetPosition;
342 }
343
344
345
346 /**
347 * Retrieves the estimated total number of entries in the result set.
348 *
349 * @return The estimated total number of entries in the result set.
350 */
351 public int getContentCount()
352 {
353 return contentCount;
354 }
355
356
357
358 /**
359 * Retrieves the result code for this virtual list view response control.
360 *
361 * @return The result code for this virtual list view response control.
362 */
363 public ResultCode getResultCode()
364 {
365 return resultCode;
366 }
367
368
369
370 /**
371 * Retrieves the context ID for this virtual list view response control, if
372 * available.
373 *
374 * @return The context ID for this virtual list view response control, or
375 * {@code null} if none was provided.
376 */
377 public ASN1OctetString getContextID()
378 {
379 return contextID;
380 }
381
382
383
384 /**
385 * {@inheritDoc}
386 */
387 @Override()
388 public String getControlName()
389 {
390 return INFO_CONTROL_NAME_VLV_RESPONSE.get();
391 }
392
393
394
395 /**
396 * {@inheritDoc}
397 */
398 @Override()
399 public void toString(final StringBuilder buffer)
400 {
401 buffer.append("VirtualListViewResponseControl(targetPosition=");
402 buffer.append(targetPosition);
403 buffer.append(", contentCount=");
404 buffer.append(contentCount);
405 buffer.append(", resultCode=");
406 buffer.append(resultCode);
407 buffer.append(')');
408 }
409 }