001/* 002 * Copyright 2010-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2010-2014 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.controls; 022 023 024 025import java.text.ParseException; 026import java.util.ArrayList; 027import java.util.UUID; 028 029import com.unboundid.asn1.ASN1Element; 030import com.unboundid.asn1.ASN1Enumerated; 031import com.unboundid.asn1.ASN1OctetString; 032import com.unboundid.asn1.ASN1Sequence; 033import com.unboundid.ldap.sdk.Control; 034import com.unboundid.ldap.sdk.DecodeableControl; 035import com.unboundid.ldap.sdk.LDAPException; 036import com.unboundid.ldap.sdk.ResultCode; 037import com.unboundid.ldap.sdk.SearchResultEntry; 038import com.unboundid.ldap.sdk.SearchResultReference; 039import com.unboundid.util.Debug; 040import com.unboundid.util.NotMutable; 041import com.unboundid.util.StaticUtils; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044import com.unboundid.util.Validator; 045 046import 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) 062public 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}