001 /*
002 * Copyright 2010-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2010-2013 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.controls;
022
023
024
025 import java.text.ParseException;
026 import java.util.ArrayList;
027 import java.util.UUID;
028
029 import com.unboundid.asn1.ASN1Element;
030 import com.unboundid.asn1.ASN1Enumerated;
031 import com.unboundid.asn1.ASN1OctetString;
032 import com.unboundid.asn1.ASN1Sequence;
033 import com.unboundid.ldap.sdk.Control;
034 import com.unboundid.ldap.sdk.DecodeableControl;
035 import com.unboundid.ldap.sdk.LDAPException;
036 import com.unboundid.ldap.sdk.ResultCode;
037 import com.unboundid.ldap.sdk.SearchResultEntry;
038 import com.unboundid.ldap.sdk.SearchResultReference;
039 import com.unboundid.util.Debug;
040 import com.unboundid.util.NotMutable;
041 import com.unboundid.util.StaticUtils;
042 import com.unboundid.util.ThreadSafety;
043 import com.unboundid.util.ThreadSafetyLevel;
044 import com.unboundid.util.Validator;
045
046 import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
047
048
049
050 /**
051 * This class provides an implementation of the LDAP content synchronization
052 * state control as defined in
053 * <a href="http://www.ietf.org/rfc/rfc4533.txt">RFC 4533</a>. Directory
054 * servers may include this control in search result entry and search result
055 * reference messages returned for a search request containing the content
056 * synchronization request control. See the documentation for the
057 * {@link ContentSyncRequestControl} class for more information information
058 * about using the content synchronization operation.
059 */
060 @NotMutable()
061 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
062 public final class ContentSyncStateControl
063 extends Control
064 implements DecodeableControl
065 {
066 /**
067 * The OID (1.3.6.1.4.1.4203.1.9.1.2) for the sync state control.
068 */
069 public static final String SYNC_STATE_OID = "1.3.6.1.4.1.4203.1.9.1.2";
070
071
072
073 /**
074 * The serial version UID for this serializable class.
075 */
076 private static final long serialVersionUID = 4796325788870542241L;
077
078
079
080 // The synchronization state cookie.
081 private final ASN1OctetString cookie;
082
083 // The synchronization state for the associated entry.
084 private final ContentSyncState state;
085
086 // The entryUUID value for the associated entry.
087 private final UUID entryUUID;
088
089
090
091 /**
092 * Creates a new empty control instance that is intended to be used only for
093 * decoding controls via the {@code DecodeableControl} interface.
094 */
095 ContentSyncStateControl()
096 {
097 state = null;
098 entryUUID = null;
099 cookie = null;
100 }
101
102
103
104 /**
105 * Creates a new content synchronization state control that provides
106 * information about a search result entry or referenced returned by a search
107 * containing the content synchronization request control.
108 *
109 * @param state The sync state for the associated entry or reference.
110 * It must not be {@code null}.
111 * @param entryUUID The entryUUID for the associated entry or reference. It
112 * must not be {@code null}.
113 * @param cookie A cookie with an updated synchronization state. It may
114 * be {@code null} if no updated state is available.
115 */
116 public ContentSyncStateControl(final ContentSyncState state,
117 final UUID entryUUID,
118 final ASN1OctetString cookie)
119 {
120 super(SYNC_STATE_OID, false, encodeValue(state, entryUUID, cookie));
121
122 this.state = state;
123 this.entryUUID = entryUUID;
124 this.cookie = cookie;
125 }
126
127
128
129 /**
130 * Creates a new content synchronization state control which is decoded from
131 * the provided information from a generic control.
132 *
133 * @param oid The OID for the control used to create this control.
134 * @param isCritical Indicates whether the control is marked critical.
135 * @param value The encoded value for the control.
136 *
137 * @throws LDAPException If the provided control cannot be decoded as a
138 * content synchronization state control.
139 */
140 public ContentSyncStateControl(final String oid, final boolean isCritical,
141 final ASN1OctetString value)
142 throws LDAPException
143 {
144 super(oid, isCritical, value);
145
146 if (value == null)
147 {
148 throw new LDAPException(ResultCode.DECODING_ERROR,
149 ERR_SYNC_STATE_NO_VALUE.get());
150 }
151
152 try
153 {
154 final ASN1Element[] elements =
155 ASN1Sequence.decodeAsSequence(value.getValue()).elements();
156
157 final ASN1Enumerated e = ASN1Enumerated.decodeAsEnumerated(elements[0]);
158 state = ContentSyncState.valueOf(e.intValue());
159 if (state == null)
160 {
161 throw new LDAPException(ResultCode.DECODING_ERROR,
162 ERR_SYNC_STATE_VALUE_INVALID_STATE.get(e.intValue()));
163 }
164
165 try
166 {
167 entryUUID = StaticUtils.decodeUUID(elements[1].getValue());
168 }
169 catch (final ParseException pe)
170 {
171 Debug.debugException(pe);
172 throw new LDAPException(ResultCode.DECODING_ERROR,
173 ERR_SYNC_STATE_VALUE_MALFORMED_UUID.get(pe.getMessage()), pe);
174 }
175
176 if (elements.length == 3)
177 {
178 cookie = ASN1OctetString.decodeAsOctetString(elements[2]);
179 }
180 else
181 {
182 cookie = null;
183 }
184 }
185 catch (final LDAPException le)
186 {
187 throw le;
188 }
189 catch (final Exception e)
190 {
191 Debug.debugException(e);
192
193 throw new LDAPException(ResultCode.DECODING_ERROR,
194 ERR_SYNC_STATE_VALUE_CANNOT_DECODE.get(
195 StaticUtils.getExceptionMessage(e)), e);
196 }
197 }
198
199
200
201 /**
202 * Encodes the provided information into a form suitable for use as the value
203 * of this control.
204 *
205 * @param state The sync state for the associated entry or reference.
206 * It must not be {@code null}.
207 * @param entryUUID The entryUUID for the associated entry or reference. It
208 * must not be {@code null}.
209 * @param cookie A cookie with an updated synchronization state. It may
210 * be {@code null} if no updated state is available.
211 *
212 * @return An ASN.1 octet string containing the encoded control value.
213 */
214 private static ASN1OctetString encodeValue(final ContentSyncState state,
215 final UUID entryUUID,
216 final ASN1OctetString cookie)
217 {
218 Validator.ensureNotNull(state, entryUUID);
219
220 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
221 elements.add(new ASN1Enumerated(state.intValue()));
222 elements.add(new ASN1OctetString(StaticUtils.encodeUUID(entryUUID)));
223
224 if (cookie != null)
225 {
226 elements.add(cookie);
227 }
228
229 return new ASN1OctetString(new ASN1Sequence(elements).encode());
230 }
231
232
233
234 /**
235 * {@inheritDoc}
236 */
237 public ContentSyncStateControl decodeControl(final String oid,
238 final boolean isCritical,
239 final ASN1OctetString value)
240 throws LDAPException
241 {
242 return new ContentSyncStateControl(oid, isCritical, value);
243 }
244
245
246
247 /**
248 * Extracts a content sync state control from the provided search result
249 * entry.
250 *
251 * @param entry The search result entry from which to retrieve the content
252 * sync state control.
253 *
254 * @return The content sync state control contained in the provided search
255 * result entry, or {@code null} if the entry did not contain a
256 * content sync state control.
257 *
258 * @throws LDAPException If a problem is encountered while attempting to
259 * decode the content sync state control contained in
260 * the provided search result entry.
261 */
262 public static ContentSyncStateControl get(final SearchResultEntry entry)
263 throws LDAPException
264 {
265 final Control c = entry.getControl(SYNC_STATE_OID);
266 if (c == null)
267 {
268 return null;
269 }
270
271 if (c instanceof ContentSyncStateControl)
272 {
273 return (ContentSyncStateControl) c;
274 }
275 else
276 {
277 return new ContentSyncStateControl(c.getOID(), c.isCritical(),
278 c.getValue());
279 }
280 }
281
282
283
284 /**
285 * Extracts a content sync state control from the provided search result
286 * reference.
287 *
288 * @param ref The search result reference from which to retrieve the content
289 * sync state control.
290 *
291 * @return The content sync state control contained in the provided search
292 * result reference, or {@code null} if the reference did not contain
293 * a content sync state control.
294 *
295 * @throws LDAPException If a problem is encountered while attempting to
296 * decode the content sync state control contained in
297 * the provided search result reference.
298 */
299 public static ContentSyncStateControl get(final SearchResultReference ref)
300 throws LDAPException
301 {
302 final Control c = ref.getControl(SYNC_STATE_OID);
303 if (c == null)
304 {
305 return null;
306 }
307
308 if (c instanceof ContentSyncStateControl)
309 {
310 return (ContentSyncStateControl) c;
311 }
312 else
313 {
314 return new ContentSyncStateControl(c.getOID(), c.isCritical(),
315 c.getValue());
316 }
317 }
318
319
320
321 /**
322 * Retrieves the synchronization state for this control, which provides
323 * information about the state of the associated search result entry or
324 * reference.
325 *
326 * @return The state value for this content synchronization state control.
327 */
328 public ContentSyncState getState()
329 {
330 return state;
331 }
332
333
334
335 /**
336 * Retrieves the entryUUID for the associated search result entry or
337 * reference.
338 *
339 * @return The entryUUID for the associated search result entry or
340 * reference.
341 */
342 public UUID getEntryUUID()
343 {
344 return entryUUID;
345 }
346
347
348
349 /**
350 * Retrieves a cookie providing updated state information for the
351 * synchronization session, if available.
352 *
353 * @return A cookie providing updated state information for the
354 * synchronization session, or {@code null} if none was included in
355 * the control.
356 */
357 public ASN1OctetString getCookie()
358 {
359 return cookie;
360 }
361
362
363
364 /**
365 * {@inheritDoc}
366 */
367 @Override()
368 public String getControlName()
369 {
370 return INFO_CONTROL_NAME_CONTENT_SYNC_STATE.get();
371 }
372
373
374
375 /**
376 * {@inheritDoc}
377 */
378 @Override()
379 public void toString(final StringBuilder buffer)
380 {
381 buffer.append("ContentSyncStateControl(state='");
382 buffer.append(state.name());
383 buffer.append("', entryUUID='");
384 buffer.append(entryUUID);
385 buffer.append('\'');
386
387 if (cookie != null)
388 {
389 buffer.append(", cookie=");
390 StaticUtils.toHex(cookie.getValue(), buffer);
391 }
392
393 buffer.append(')');
394 }
395 }