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