001/* 002 * Copyright 2013-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2013-2017 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 */ 021package com.unboundid.ldap.sdk.experimental; 022 023 024 025import com.unboundid.asn1.ASN1Element; 026import com.unboundid.asn1.ASN1Integer; 027import com.unboundid.asn1.ASN1OctetString; 028import com.unboundid.asn1.ASN1Sequence; 029import com.unboundid.ldap.sdk.Control; 030import com.unboundid.ldap.sdk.DecodeableControl; 031import com.unboundid.ldap.sdk.LDAPException; 032import com.unboundid.ldap.sdk.ResultCode; 033import com.unboundid.ldap.sdk.SearchResult; 034import com.unboundid.util.Debug; 035import com.unboundid.util.NotMutable; 036import com.unboundid.util.StaticUtils; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*; 041 042 043 044/** 045 * This class provides support for a control that may be used to poll an Active 046 * Directory Server for information about changes that have been processed. Use 047 * of this control is documented at 048 * <A HREF="http://support.microsoft.com/kb/891995"> 049 * http://support.microsoft.com/kb/891995</A> and at 050 * <A HREF="http://msdn.microsoft.com/en-us/library/ms677626.aspx"> 051 * http://msdn.microsoft.com/en-us/library/ms677626.aspx</A>. The control OID 052 * and value format are described at 053 * <A HREF="http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx"> 054 * http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx</A> and the 055 * values of the flags are documented at 056 * <A HREF="http://msdn.microsoft.com/en-us/library/cc223347.aspx"> 057 * http://msdn.microsoft.com/en-us/library/cc223347.aspx</A>. 058 * <BR><BR> 059 * <H2>Example</H2> 060 * The following example demonstrates the process for using the DirSync control 061 * to identify changes to user entries below "dc=example,dc=com": 062 * <PRE> 063 * // Create a search request that will be used to identify all users below 064 * // "dc=example,dc=com". 065 * final SearchRequest searchRequest = new SearchRequest("dc=example,dc=com", 066 * SearchScope.SUB, Filter.createEqualityFilter("objectClass", "User")); 067 * 068 * // Define the components that will be included in the DirSync request 069 * // control. 070 * ASN1OctetString cookie = null; 071 * final int flags = ActiveDirectoryDirSyncControl.FLAG_INCREMENTAL_VALUES | 072 * ActiveDirectoryDirSyncControl.FLAG_OBJECT_SECURITY; 073 * 074 * // Create a loop that will be used to keep polling for changes. 075 * while (keepLooping) 076 * { 077 * // Update the controls that will be used for the search request. 078 * searchRequest.setControls(new ActiveDirectoryDirSyncControl(true, flags, 079 * 50, cookie)); 080 * 081 * // Process the search and get the response control. 082 * final SearchResult searchResult = connection.search(searchRequest); 083 * ActiveDirectoryDirSyncControl dirSyncResponse = 084 * ActiveDirectoryDirSyncControl.get(searchResult); 085 * cookie = dirSyncResponse.getCookie(); 086 * 087 * // Process the search result entries because they represent entries that 088 * // have been created or modified. 089 * for (final SearchResultEntry updatedEntry : 090 * searchResult.getSearchEntries()) 091 * { 092 * // Do something with the entry. 093 * } 094 * 095 * // If the client might want to continue the search even after shutting 096 * // down and starting back up later, then persist the cookie now. 097 * } 098 * </PRE> 099 */ 100@NotMutable() 101@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 102public final class ActiveDirectoryDirSyncControl 103 extends Control 104 implements DecodeableControl 105{ 106 /** 107 * The OID (1.2.840.113556.1.4.841) for the DirSync control. 108 */ 109 public static final String DIRSYNC_OID = "1.2.840.113556.1.4.841"; 110 111 112 113 /** 114 * The value of the flag that indicates that the client should only be allowed 115 * to view objects and attributes that are otherwise accessible to the client. 116 */ 117 public static final int FLAG_OBJECT_SECURITY = 0x00000001; 118 119 120 121 /** 122 * The value of the flag that indicates the server should return parent 123 * objects before child objects. 124 */ 125 public static final int FLAG_ANCESTORS_FIRST_ORDER = 0x00000800; 126 127 128 129 /** 130 * The value of the flag that indicates that the server should not return 131 * private data in search results. 132 */ 133 public static final int FLAG_PUBLIC_DATA_ONLY = 0x00002000; 134 135 136 137 /** 138 * The value of the flag that indicates that only changed values of attributes 139 * should be included in search results. 140 */ 141 public static final int FLAG_INCREMENTAL_VALUES = 0x80000000; 142 143 144 145 /** 146 * The serial version UID for this serializable class. 147 */ 148 private static final long serialVersionUID = -2871267685237800654L; 149 150 151 152 // A cookie that may be used to resume a previous DirSync search. 153 private final ASN1OctetString cookie; 154 155 // The value of the flags that should be used for DirSync operation. 156 private final int flags; 157 158 // The maximum number of attributes to return. 159 private final int maxAttributeCount; 160 161 162 163 /** 164 * Creates a new empty control instance that is intended to be used only for 165 * decoding controls via the {@code DecodeableControl} interface. 166 */ 167 ActiveDirectoryDirSyncControl() 168 { 169 this(true, 0, 0, null); 170 } 171 172 173 174 /** 175 * Creates a new DirSync control with the provided information. 176 * 177 * @param isCritical Indicates whether this control should be marked 178 * critical. 179 * @param flags The value of the flags that should be used for 180 * DirSync operation. This should be zero if no 181 * special flags or needed, or a bitwise OR of the 182 * values of the individual flags that are desired. 183 * @param maxAttributeCount The maximum number of attributes to return. 184 * @param cookie A cookie that may be used to resume a previous 185 * DirSync search. This may be {@code null} if 186 * no previous cookie is available. 187 */ 188 public ActiveDirectoryDirSyncControl(final boolean isCritical, 189 final int flags, 190 final int maxAttributeCount, 191 final ASN1OctetString cookie) 192 { 193 super(DIRSYNC_OID, isCritical, 194 encodeValue(flags, maxAttributeCount, cookie)); 195 196 this.flags = flags; 197 this.maxAttributeCount = maxAttributeCount; 198 199 if (cookie == null) 200 { 201 this.cookie = new ASN1OctetString(); 202 } 203 else 204 { 205 this.cookie = cookie; 206 } 207 } 208 209 210 211 /** 212 * Creates a new DirSync control with settings decoded from the provided 213 * control information. 214 * 215 * @param oid The OID of the control to be decoded. 216 * @param isCritical The criticality of the control to be decoded. 217 * @param value The value of the control to be decoded. 218 * 219 * @throws LDAPException If a problem is encountered while attempting to 220 * decode the control value as appropriate for a 221 * DirSync control. 222 */ 223 public ActiveDirectoryDirSyncControl(final String oid, 224 final boolean isCritical, 225 final ASN1OctetString value) 226 throws LDAPException 227 { 228 super(oid, isCritical, value); 229 230 if (value == null) 231 { 232 throw new LDAPException(ResultCode.DECODING_ERROR, 233 ERR_DIRSYNC_CONTROL_NO_VALUE.get()); 234 } 235 236 try 237 { 238 final ASN1Element[] elements = 239 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 240 flags = ASN1Integer.decodeAsInteger(elements[0]).intValue(); 241 maxAttributeCount = ASN1Integer.decodeAsInteger(elements[1]).intValue(); 242 cookie = ASN1OctetString.decodeAsOctetString(elements[2]); 243 } 244 catch (final Exception e) 245 { 246 Debug.debugException(e); 247 248 throw new LDAPException(ResultCode.DECODING_ERROR, 249 ERR_DIRSYNC_CONTROL_DECODE_ERROR.get( 250 StaticUtils.getExceptionMessage(e)), 251 e); 252 } 253 } 254 255 256 257 /** 258 * Encodes the provided information into a format appropriate for use as the 259 * value of a DirSync control. 260 * 261 * @param flags The value of the flags that should be used for 262 * DirSync operation. This should be zero if no 263 * special flags or needed, or a bitwise OR of the 264 * values of the individual flags that are desired. 265 * @param maxAttributeCount The maximum number of attributes to return. 266 * @param cookie A cookie that may be used to resume a previous 267 * DirSync search. This may be {@code null} if 268 * no previous cookie is available. 269 * 270 * @return An ASN.1 octet string containing the encoded control value. 271 */ 272 private static ASN1OctetString encodeValue(final int flags, 273 final int maxAttributeCount, 274 final ASN1OctetString cookie) 275 { 276 final ASN1Element[] valueElements = new ASN1Element[3]; 277 valueElements[0] = new ASN1Integer(flags); 278 valueElements[1] = new ASN1Integer(maxAttributeCount); 279 280 if (cookie == null) 281 { 282 valueElements[2] = new ASN1OctetString(); 283 } 284 else 285 { 286 valueElements[2] = cookie; 287 } 288 289 return new ASN1OctetString(new ASN1Sequence(valueElements).encode()); 290 } 291 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 public ActiveDirectoryDirSyncControl decodeControl(final String oid, 298 final boolean isCritical, 299 final ASN1OctetString value) 300 throws LDAPException 301 { 302 return new ActiveDirectoryDirSyncControl(oid, isCritical, value); 303 } 304 305 306 307 308 /** 309 * Retrieves the value of the flags that should be used for DirSync operation. 310 * 311 * @return The value of the flags that should be used for DirSync operation. 312 */ 313 public int getFlags() 314 { 315 return flags; 316 } 317 318 319 320 /** 321 * Retrieves the maximum number of attributes to return. 322 * 323 * @return The maximum number of attributes to return. 324 */ 325 public int getMaxAttributeCount() 326 { 327 return maxAttributeCount; 328 } 329 330 331 332 /** 333 * Retrieves a cookie that may be used to resume a previous DirSync search, 334 * if available. 335 * 336 * @return A cookie that may be used to resume a previous DirSync search, or 337 * a zero-length cookie if there is none. 338 */ 339 public ASN1OctetString getCookie() 340 { 341 return cookie; 342 } 343 344 345 346 /** 347 * Extracts a DirSync response control from the provided result. 348 * 349 * @param result The result from which to retrieve the DirSync response 350 * control. 351 * 352 * @return The DirSync response control contained in the provided result, or 353 * {@code null} if the result did not include a DirSync response 354 * control. 355 * 356 * @throws LDAPException If a problem is encountered while attempting to 357 * decode the DirSync response control contained in 358 * the provided result. 359 */ 360 public static ActiveDirectoryDirSyncControl get(final SearchResult result) 361 throws LDAPException 362 { 363 final Control c = result.getResponseControl(DIRSYNC_OID); 364 if (c == null) 365 { 366 return null; 367 } 368 369 if (c instanceof ActiveDirectoryDirSyncControl) 370 { 371 return (ActiveDirectoryDirSyncControl) c; 372 } 373 else 374 { 375 return new ActiveDirectoryDirSyncControl(c.getOID(), c.isCritical(), 376 c.getValue()); 377 } 378 } 379 380 381 382 /** 383 * {@inheritDoc} 384 */ 385 @Override() 386 public String getControlName() 387 { 388 return INFO_CONTROL_NAME_DIRSYNC.get(); 389 } 390 391 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override() 397 public void toString(final StringBuilder buffer) 398 { 399 buffer.append("ActiveDirectoryDirSyncControl(isCritical="); 400 buffer.append(isCritical()); 401 buffer.append(", flags="); 402 buffer.append(flags); 403 buffer.append(", maxAttributeCount="); 404 buffer.append(maxAttributeCount); 405 buffer.append(", cookie=byte["); 406 buffer.append(cookie.getValueLength()); 407 buffer.append("])"); 408 } 409}