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