001 /*
002 * Copyright 2013-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2013-2016 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.experimental;
022
023
024
025 import com.unboundid.asn1.ASN1Element;
026 import com.unboundid.asn1.ASN1Integer;
027 import com.unboundid.asn1.ASN1OctetString;
028 import com.unboundid.asn1.ASN1Sequence;
029 import com.unboundid.ldap.sdk.Control;
030 import com.unboundid.ldap.sdk.DecodeableControl;
031 import com.unboundid.ldap.sdk.LDAPException;
032 import com.unboundid.ldap.sdk.ResultCode;
033 import com.unboundid.ldap.sdk.SearchResult;
034 import com.unboundid.util.Debug;
035 import com.unboundid.util.NotMutable;
036 import com.unboundid.util.StaticUtils;
037 import com.unboundid.util.ThreadSafety;
038 import com.unboundid.util.ThreadSafetyLevel;
039
040 import 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)
102 public 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 }