001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.controls; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.ArrayList; 033 034 import org.opends.server.protocols.asn1.ASN1Constants; 035 import org.opends.server.protocols.asn1.ASN1Element; 036 import org.opends.server.protocols.asn1.ASN1Enumerated; 037 import org.opends.server.protocols.asn1.ASN1Long; 038 import org.opends.server.protocols.asn1.ASN1OctetString; 039 import org.opends.server.protocols.asn1.ASN1Sequence; 040 import org.opends.server.protocols.ldap.LDAPResultCode; 041 import org.opends.server.types.Control; 042 import org.opends.server.types.DN; 043 import org.opends.server.types.DebugLogLevel; 044 import org.opends.server.types.LDAPException; 045 046 import static org.opends.server.loggers.debug.DebugLogger.*; 047 import org.opends.server.loggers.debug.DebugTracer; 048 import static org.opends.messages.ProtocolMessages.*; 049 import static org.opends.server.util.ServerConstants.*; 050 import static org.opends.server.util.StaticUtils.*; 051 052 053 054 /** 055 * This class implements the entry change notification control defined in 056 * draft-ietf-ldapext-psearch. It may be included in entries returned in 057 * response to a persistent search operation. 058 */ 059 public class EntryChangeNotificationControl 060 extends Control 061 { 062 /** 063 * The tracer object for the debug logger. 064 */ 065 private static final DebugTracer TRACER = getTracer(); 066 067 068 069 070 // The previous DN for this change notification control. 071 private DN previousDN; 072 073 // The change number for this change notification control. 074 private long changeNumber; 075 076 // The change type for this change notification control. 077 private PersistentSearchChangeType changeType; 078 079 080 081 /** 082 * Creates a new entry change notification control with the provided 083 * information. 084 * 085 * @param changeType The change type for this change notification control. 086 * @param changeNumber The change number for the associated change, or a 087 * negative value if no change number is available. 088 */ 089 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 090 long changeNumber) 091 { 092 super(OID_ENTRY_CHANGE_NOTIFICATION, false, 093 encodeValue(changeType, null, changeNumber)); 094 095 096 this.changeType = changeType; 097 this.changeNumber = changeNumber; 098 099 previousDN = null; 100 } 101 102 103 104 /** 105 * Creates a new entry change notification control with the provided 106 * information. 107 * 108 * @param changeType The change type for this change notification control. 109 * @param previousDN The DN that the entry had prior to a modify DN 110 * operation, or <CODE>null</CODE> if the operation was 111 * not a modify DN. 112 * @param changeNumber The change number for the associated change, or a 113 * negative value if no change number is available. 114 */ 115 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 116 DN previousDN, long changeNumber) 117 { 118 super(OID_ENTRY_CHANGE_NOTIFICATION, false, 119 encodeValue(changeType, previousDN, changeNumber)); 120 121 122 this.changeType = changeType; 123 this.previousDN = previousDN; 124 this.changeNumber = changeNumber; 125 } 126 127 128 129 /** 130 * Creates a new entry change notification control with the provided 131 * information. 132 * 133 * @param oid The OID to use for this control. 134 * @param isCritical Indicates whether this control should be considered 135 * critical to the operation processing. 136 * @param changeType The change type for this change notification control. 137 * @param previousDN The DN that the entry had prior to a modify DN 138 * operation, or <CODE>null</CODE> if the operation was 139 * not a modify DN. 140 * @param changeNumber The change number for the associated change, or a 141 * negative value if no change number is available. 142 */ 143 public EntryChangeNotificationControl(String oid, boolean isCritical, 144 PersistentSearchChangeType changeType, 145 DN previousDN, long changeNumber) 146 { 147 super(oid, isCritical, encodeValue(changeType, previousDN, changeNumber)); 148 149 150 this.changeType = changeType; 151 this.previousDN = previousDN; 152 this.changeNumber = changeNumber; 153 } 154 155 156 157 /** 158 * Creates a new entry change notification control with the provided 159 * information. 160 * 161 * @param oid The OID to use for this control. 162 * @param isCritical Indicates whether this control should be considered 163 * critical to the operation processing. 164 * @param changeType The change type for this change notification control. 165 * @param previousDN The DN that the entry had prior to a modify DN 166 * operation, or <CODE>null</CODE> if the operation was 167 * not a modify DN. 168 * @param changeNumber The change number for the associated change, or a 169 * negative value if no change number is available. 170 * @param encodedValue The pre-encoded value for this change notification 171 * control. 172 */ 173 private EntryChangeNotificationControl(String oid, boolean isCritical, 174 PersistentSearchChangeType changeType, 175 DN previousDN, long changeNumber, 176 ASN1OctetString encodedValue) 177 { 178 super(oid, isCritical, encodedValue); 179 180 181 this.changeType = changeType; 182 this.previousDN = previousDN; 183 this.changeNumber = changeNumber; 184 } 185 186 187 188 /** 189 * Encodes the provided information into an ASN.1 octet string suitable for 190 * use as the control value. 191 * 192 * @param changeType The change type for this change notification control. 193 * @param previousDN The DN that the entry had prior to a modify DN 194 * operation, or <CODE>null</CODE> if the operation was 195 * not a modify DN. 196 * @param changeNumber The change number for the associated change, or a 197 * negative value if no change number is available. 198 * 199 * @return An ASN.1 octet string containing the encoded information. 200 */ 201 private static ASN1OctetString encodeValue(PersistentSearchChangeType 202 changeType, 203 DN previousDN, long changeNumber) 204 { 205 ArrayList<ASN1Element> elements = 206 new ArrayList<ASN1Element>(3); 207 elements.add(new ASN1Enumerated(changeType.intValue())); 208 209 if (previousDN != null) 210 { 211 elements.add(new ASN1OctetString(previousDN.toString())); 212 } 213 214 if (changeNumber > 0) 215 { 216 elements.add(new ASN1Long(changeNumber)); 217 } 218 219 220 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 221 } 222 223 224 225 /** 226 * Creates a new entry change notification control from the contents of the 227 * provided control. 228 * 229 * @param control The generic control containing the information to use to 230 * create this entry change notification control. 231 * 232 * @return The entry change notification control decoded from the provided 233 * control. 234 * 235 * @throws LDAPException If this control cannot be decoded as a valid 236 * entry change notification control. 237 */ 238 public static EntryChangeNotificationControl decodeControl(Control control) 239 throws LDAPException 240 { 241 if (! control.hasValue()) 242 { 243 Message message = ERR_ECN_NO_CONTROL_VALUE.get(); 244 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 245 } 246 247 248 DN previousDN = null; 249 long changeNumber = -1; 250 PersistentSearchChangeType changeType; 251 try 252 { 253 ArrayList<ASN1Element> elements = 254 ASN1Sequence.decodeAsSequence(control.getValue().value()).elements(); 255 if ((elements.size() < 1) || (elements.size() > 3)) 256 { 257 Message message = ERR_ECN_INVALID_ELEMENT_COUNT.get(elements.size()); 258 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 259 } 260 261 int changeTypeValue = elements.get(0).decodeAsEnumerated().intValue(); 262 changeType = PersistentSearchChangeType.valueOf(changeTypeValue); 263 264 if (elements.size() == 2) 265 { 266 ASN1Element e = elements.get(1); 267 if (e.getType() == ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE) 268 { 269 if (changeType != PersistentSearchChangeType.MODIFY_DN) 270 { 271 Message message = 272 ERR_ECN_ILLEGAL_PREVIOUS_DN.get(String.valueOf(changeType)); 273 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 274 } 275 276 ASN1OctetString rawPreviousDN = e.decodeAsOctetString(); 277 previousDN = DN.decode(rawPreviousDN); 278 } 279 else if (e.getType() == ASN1Constants.UNIVERSAL_INTEGER_TYPE) 280 { 281 changeNumber = e.decodeAsLong().longValue(); 282 } 283 else 284 { 285 Message message = 286 ERR_ECN_INVALID_ELEMENT_TYPE.get(byteToHex(e.getType())); 287 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 288 } 289 } 290 else if (elements.size() == 3) 291 { 292 if (changeType != PersistentSearchChangeType.MODIFY_DN) 293 { 294 Message message = 295 ERR_ECN_ILLEGAL_PREVIOUS_DN.get(String.valueOf(changeType)); 296 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 297 } 298 299 ASN1OctetString rawPreviousDN = elements.get(1).decodeAsOctetString(); 300 previousDN = DN.decode(rawPreviousDN); 301 302 changeNumber = elements.get(2).decodeAsLong().longValue(); 303 } 304 } 305 catch (LDAPException le) 306 { 307 throw le; 308 } 309 catch (Exception e) 310 { 311 if (debugEnabled()) 312 { 313 TRACER.debugCaught(DebugLogLevel.ERROR, e); 314 } 315 316 Message message = ERR_ECN_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 317 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 318 } 319 320 321 return new EntryChangeNotificationControl(control.getOID(), 322 control.isCritical(), changeType, 323 previousDN, changeNumber, 324 control.getValue()); 325 } 326 327 328 329 /** 330 * Retrieves the change type for this entry change notification control. 331 * 332 * @return The change type for this entry change notification control. 333 */ 334 public PersistentSearchChangeType getChangeType() 335 { 336 return changeType; 337 } 338 339 340 341 /** 342 * Sets the change type for this entry change notification control. 343 * 344 * @param changeType The change type for this entry change notification 345 * control. 346 */ 347 public void setChangeType(PersistentSearchChangeType changeType) 348 { 349 this.changeType = changeType; 350 351 setValue(encodeValue(changeType, previousDN, changeNumber)); 352 } 353 354 355 356 /** 357 * Retrieves the previous DN for this entry change notification control. 358 * 359 * @return The previous DN for this entry change notification control, or 360 * <CODE>null</CODE> if there is none. 361 */ 362 public DN getPreviousDN() 363 { 364 return previousDN; 365 } 366 367 368 369 /** 370 * Specifies the previous DN for this entry change notification control. 371 * 372 * @param previousDN The previous DN for this entry change notification 373 * control. 374 */ 375 public void setPreviousDN(DN previousDN) 376 { 377 this.previousDN = previousDN; 378 379 setValue(encodeValue(changeType, previousDN, changeNumber)); 380 } 381 382 383 384 /** 385 * Retrieves the change number for this entry change notification control. 386 * 387 * @return The change number for this entry change notification control, or a 388 * negative value if no change number is available. 389 */ 390 public long getChangeNumber() 391 { 392 return changeNumber; 393 } 394 395 396 397 /** 398 * Specifies the change number for this entry change notification control. 399 * 400 * @param changeNumber The change number for this entry change notification 401 * control. 402 */ 403 public void setChangeNumber(long changeNumber) 404 { 405 this.changeNumber = changeNumber; 406 407 setValue(encodeValue(changeType, previousDN, changeNumber)); 408 } 409 410 411 412 /** 413 * Retrieves a string representation of this entry change notification 414 * control. 415 * 416 * @return A string representation of this entry change notification control. 417 */ 418 public String toString() 419 { 420 StringBuilder buffer = new StringBuilder(); 421 toString(buffer); 422 return buffer.toString(); 423 } 424 425 426 427 /** 428 * Appends a string representation of this entry change notification control 429 * to the provided buffer. 430 * 431 * @param buffer The buffer to which the information should be appended. 432 */ 433 public void toString(StringBuilder buffer) 434 { 435 buffer.append("EntryChangeNotificationControl(changeType="); 436 buffer.append(changeType.toString()); 437 438 if (previousDN != null) 439 { 440 buffer.append(",previousDN=\""); 441 buffer.append(previousDN.toString()); 442 buffer.append("\""); 443 } 444 445 if (changeNumber > 0) 446 { 447 buffer.append(",changeNumber="); 448 buffer.append(changeNumber); 449 } 450 451 buffer.append(")"); 452 } 453 } 454