001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 Ping Identity Corporation 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 */ 021package com.unboundid.ldap.sdk.unboundidds.controls; 022 023 024 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.List; 030import java.util.Iterator; 031 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1OctetString; 034import com.unboundid.asn1.ASN1Sequence; 035import com.unboundid.ldap.sdk.Control; 036import com.unboundid.ldap.sdk.LDAPException; 037import com.unboundid.ldap.sdk.ResultCode; 038import com.unboundid.util.Debug; 039import com.unboundid.util.NotMutable; 040import com.unboundid.util.StaticUtils; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043import com.unboundid.util.Validator; 044 045import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 046 047 048 049/** 050 * This class provides a request control which may be used to request that 051 * entries below one or more base DNs be excluded from the results returned to 052 * a client while processing a search operation. For example, this may be 053 * useful in cases where you want to perform a search below "dc=example,dc=com", 054 * but want to exclude all entries below "ou=private,dc=example,dc=com". 055 * <BR> 056 * <BLOCKQUOTE> 057 * <B>NOTE:</B> This class, and other classes within the 058 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 059 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 060 * server products. These classes provide support for proprietary 061 * functionality or for external specifications that are not considered stable 062 * or mature enough to be guaranteed to work in an interoperable way with 063 * other types of LDAP servers. 064 * </BLOCKQUOTE> 065 * <BR> 066 * The criticality for this control may be either {@code true} or {@code false}. 067 * It must have a value with the following encoding: 068 * <PRE> 069 * ExcludeBranchRequest ::= SEQUENCE { 070 * baseDNs [0] SEQUENCE OF LDAPDN, 071 * ... } 072 * </PRE> 073 */ 074@NotMutable() 075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 076public final class ExcludeBranchRequestControl 077 extends Control 078{ 079 /** 080 * The OID (1.3.6.1.4.1.30221.2.5.17) for the exclude branch request control. 081 */ 082 public static final String EXCLUDE_BRANCH_REQUEST_OID = 083 "1.3.6.1.4.1.30221.2.5.17"; 084 085 086 087 /** 088 * The BER type for the base DNs element. 089 */ 090 private static final byte TYPE_BASE_DNS = (byte) 0xA0; 091 092 093 094 /** 095 * The serial version UID for this serializable class. 096 */ 097 private static final long serialVersionUID = -8599554860060612417L; 098 099 100 101 // The list of base DNs to be excluded from the search results. 102 private final List<String> baseDNs; 103 104 105 106 /** 107 * Creates a new exclude branch request control with the provided set of base 108 * DNs. It will be marked critical. 109 * 110 * @param baseDNs The base DNs for entries to be excluded from search 111 * results. It must not be {@code null} or empty. 112 */ 113 public ExcludeBranchRequestControl(final Collection<String> baseDNs) 114 { 115 this(true, baseDNs); 116 } 117 118 119 120 /** 121 * Creates a new exclude branch request control with the provided set of base 122 * DNs. It will be marked critical. 123 * 124 * @param baseDNs The base DNs for entries to be excluded from search 125 * results. It must not be {@code null} or empty. 126 */ 127 public ExcludeBranchRequestControl(final String... baseDNs) 128 { 129 this(true, baseDNs); 130 } 131 132 133 134 /** 135 * Creates a new exclude branch request control with the provided information. 136 * 137 * @param isCritical Indicates whether the control should be marked 138 * critical. 139 * @param baseDNs The base DNs for entries to be excluded from search 140 * results. It must not be {@code null} or empty. 141 */ 142 public ExcludeBranchRequestControl(final boolean isCritical, 143 final String... baseDNs) 144 { 145 super(EXCLUDE_BRANCH_REQUEST_OID, isCritical, encodeValue(baseDNs)); 146 147 this.baseDNs = Collections.unmodifiableList(Arrays.asList(baseDNs)); 148 } 149 150 151 152 /** 153 * Creates a new exclude branch request control with the provided information. 154 * 155 * @param isCritical Indicates whether the control should be marked 156 * critical. 157 * @param baseDNs The base DNs for entries to be excluded from search 158 * results. It must not be {@code null} or empty. 159 */ 160 public ExcludeBranchRequestControl(final boolean isCritical, 161 final Collection<String> baseDNs) 162 { 163 super(EXCLUDE_BRANCH_REQUEST_OID, isCritical, encodeValue(baseDNs)); 164 165 this.baseDNs = Collections.unmodifiableList(new ArrayList<String>(baseDNs)); 166 } 167 168 169 170 /** 171 * Creates a new exclude branch request control which is decoded from the 172 * provided generic control. 173 * 174 * @param control The generic control to be decoded as an exclude branch 175 * request control. 176 * 177 * @throws LDAPException If the provided control cannot be decoded as an 178 * exclude branch request control. 179 */ 180 public ExcludeBranchRequestControl(final Control control) 181 throws LDAPException 182 { 183 super(control); 184 185 final ASN1OctetString value = control.getValue(); 186 if (value == null) 187 { 188 throw new LDAPException(ResultCode.DECODING_ERROR, 189 ERR_EXCLUDE_BRANCH_MISSING_VALUE.get()); 190 } 191 192 final ASN1Sequence valueSequence; 193 try 194 { 195 valueSequence = ASN1Sequence.decodeAsSequence(value.getValue()); 196 } 197 catch (final Exception e) 198 { 199 Debug.debugException(e); 200 throw new LDAPException(ResultCode.DECODING_ERROR, 201 ERR_EXCLUDE_BRANCH_VALUE_NOT_SEQUENCE.get( 202 StaticUtils.getExceptionMessage(e)), e); 203 } 204 205 try 206 { 207 final ASN1Element[] elements = valueSequence.elements(); 208 209 final ASN1Element[] dnElements = 210 ASN1Sequence.decodeAsSequence(elements[0]).elements(); 211 final ArrayList<String> dnList = new ArrayList<String>(dnElements.length); 212 for (final ASN1Element e : dnElements) 213 { 214 dnList.add(ASN1OctetString.decodeAsOctetString(e).stringValue()); 215 } 216 baseDNs = Collections.unmodifiableList(dnList); 217 218 if (baseDNs.isEmpty()) 219 { 220 throw new LDAPException(ResultCode.DECODING_ERROR, 221 ERR_EXCLUDE_BRANCH_NO_BASE_DNS.get()); 222 } 223 } 224 catch (final LDAPException le) 225 { 226 Debug.debugException(le); 227 throw le; 228 } 229 catch (final Exception e) 230 { 231 Debug.debugException(e); 232 throw new LDAPException(ResultCode.DECODING_ERROR, 233 ERR_EXCLUDE_BRANCH_ERROR_PARSING_VALUE.get( 234 StaticUtils.getExceptionMessage(e)), e); 235 } 236 } 237 238 239 240 /** 241 * Encodes the provided information into a form suitable for use as the value 242 * of this control. 243 * 244 * @param baseDNs The base DNs for entries to be excluded from search 245 * results. It must not be {@code null} or empty. 246 * 247 * @return The encoded value for this control. 248 */ 249 private static ASN1OctetString encodeValue(final String... baseDNs) 250 { 251 Validator.ensureNotNull(baseDNs); 252 return encodeValue(Arrays.asList(baseDNs)); 253 } 254 255 256 257 /** 258 * Encodes the provided information into a form suitable for use as the value 259 * of this control. 260 * 261 * @param baseDNs The base DNs for entries to be excluded from search 262 * results. It must not be {@code null} or empty. 263 * 264 * @return The encoded value for this control. 265 */ 266 private static ASN1OctetString encodeValue(final Collection<String> baseDNs) 267 { 268 Validator.ensureNotNull(baseDNs); 269 Validator.ensureFalse(baseDNs.isEmpty()); 270 271 final ArrayList<ASN1Element> dnElements = 272 new ArrayList<ASN1Element>(baseDNs.size()); 273 for (final String s : baseDNs) 274 { 275 dnElements.add(new ASN1OctetString(s)); 276 } 277 278 final ASN1Sequence baseDNSequence = 279 new ASN1Sequence(TYPE_BASE_DNS, dnElements); 280 final ASN1Sequence valueSequence = new ASN1Sequence(baseDNSequence); 281 return new ASN1OctetString(valueSequence.encode()); 282 } 283 284 285 286 /** 287 * Retrieves a list of the base DNs for entries to exclude from the search 288 * results. 289 * 290 * @return A list of the base DNs for entries to exclude from the search 291 * results. 292 */ 293 public List<String> getBaseDNs() 294 { 295 return baseDNs; 296 } 297 298 299 300 /** 301 * {@inheritDoc} 302 */ 303 @Override() 304 public String getControlName() 305 { 306 return INFO_CONTROL_NAME_EXCLUDE_BRANCH.get(); 307 } 308 309 310 311 /** 312 * {@inheritDoc} 313 */ 314 @Override() 315 public void toString(final StringBuilder buffer) 316 { 317 buffer.append("ExcludeBranchRequestControl(isCritical="); 318 buffer.append(isCritical()); 319 buffer.append(", baseDNs={"); 320 321 final Iterator<String> iterator = baseDNs.iterator(); 322 while (iterator.hasNext()) 323 { 324 buffer.append('\''); 325 buffer.append(iterator.next()); 326 buffer.append('\''); 327 328 if (iterator.hasNext()) 329 { 330 buffer.append(", "); 331 } 332 } 333 334 buffer.append("})"); 335 } 336}