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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.controls; 028import org.forgerock.i18n.LocalizableMessage; 029 030 031import java.io.IOException; 032 033import org.forgerock.opendj.io.*; 034import org.opends.server.types.*; 035import org.forgerock.opendj.ldap.ResultCode; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import static org.opends.messages.ProtocolMessages.*; 039import static org.opends.server.util.ServerConstants.*; 040import static org.opends.server.util.StaticUtils.*; 041 042 043 044/** 045 * This class implements the entry change notification control defined in 046 * draft-ietf-ldapext-psearch. It may be included in entries returned in 047 * response to a persistent search operation. 048 */ 049public class EntryChangeNotificationControl 050 extends Control 051{ 052 /** 053 * ControlDecoder implementation to decode this control from a ByteString. 054 */ 055 private static final class Decoder 056 implements ControlDecoder<EntryChangeNotificationControl> 057 { 058 /** {@inheritDoc} */ 059 public EntryChangeNotificationControl decode( 060 boolean isCritical, ByteString value) throws DirectoryException 061 { 062 if (value == null) 063 { 064 LocalizableMessage message = ERR_ECN_NO_CONTROL_VALUE.get(); 065 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 066 } 067 068 069 DN previousDN = null; 070 long changeNumber = -1; 071 PersistentSearchChangeType changeType; 072 ASN1Reader reader = ASN1.getReader(value); 073 try 074 { 075 reader.readStartSequence(); 076 077 int changeTypeValue = (int)reader.readInteger(); 078 changeType = PersistentSearchChangeType.valueOf(changeTypeValue); 079 080 if(reader.hasNextElement() && 081 reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE) 082 { 083 if (changeType != PersistentSearchChangeType.MODIFY_DN) 084 { 085 LocalizableMessage message = ERR_ECN_ILLEGAL_PREVIOUS_DN.get(changeType); 086 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 087 } 088 089 previousDN = DN.valueOf(reader.readOctetStringAsString()); 090 } 091 if(reader.hasNextElement() && 092 reader.peekType() == ASN1.UNIVERSAL_INTEGER_TYPE) 093 { 094 changeNumber = reader.readInteger(); 095 } 096 } 097 catch (DirectoryException de) 098 { 099 throw de; 100 } 101 catch (Exception e) 102 { 103 logger.traceException(e); 104 105 LocalizableMessage message = 106 ERR_ECN_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 107 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e); 108 } 109 110 111 return new EntryChangeNotificationControl(isCritical, changeType, 112 previousDN, changeNumber); 113 } 114 115 public String getOID() 116 { 117 return OID_ENTRY_CHANGE_NOTIFICATION; 118 } 119 120 } 121 122 /** 123 * The ControlDecoder that can be used to decode this control. 124 */ 125 public static final ControlDecoder<EntryChangeNotificationControl> DECODER = 126 new Decoder(); 127 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 128 129 130 131 132 /** The previous DN for this change notification control. */ 133 private DN previousDN; 134 135 /** The change number for this change notification control. */ 136 private long changeNumber; 137 138 /** The change type for this change notification control. */ 139 private PersistentSearchChangeType changeType; 140 141 142 /** 143 * Creates a new entry change notification control with the provided 144 * information. 145 * 146 * @param isCritical Indicates whether this control should be 147 * considered critical in processing the 148 * request. 149 * @param changeType The change type for this change notification control. 150 * @param changeNumber The change number for the associated change, or a 151 * negative value if no change number is available. 152 */ 153 public EntryChangeNotificationControl(boolean isCritical, 154 PersistentSearchChangeType changeType, 155 long changeNumber) 156 { 157 super(OID_ENTRY_CHANGE_NOTIFICATION, isCritical); 158 159 160 this.changeType = changeType; 161 this.changeNumber = changeNumber; 162 163 previousDN = null; 164 } 165 166 167 168 /** 169 * Creates a new entry change notification control with the provided 170 * information. 171 * 172 * @param isCritical Indicates whether this control should be 173 * considered critical in processing the 174 * request. 175 * @param changeType The change type for this change notification control. 176 * @param previousDN The DN that the entry had prior to a modify DN 177 * operation, or <CODE>null</CODE> if the operation was 178 * not a modify DN. 179 * @param changeNumber The change number for the associated change, or a 180 * negative value if no change number is available. 181 */ 182 public EntryChangeNotificationControl(boolean isCritical, 183 PersistentSearchChangeType changeType, 184 DN previousDN, long changeNumber) 185 { 186 super(OID_ENTRY_CHANGE_NOTIFICATION, isCritical); 187 188 189 this.changeType = changeType; 190 this.previousDN = previousDN; 191 this.changeNumber = changeNumber; 192 } 193 194 195 /** 196 * Creates a new entry change notification control with the provided 197 * information. 198 * 199 * @param changeType The change type for this change notification control. 200 * @param changeNumber The change number for the associated change, or a 201 * negative value if no change number is available. 202 */ 203 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 204 long changeNumber) 205 { 206 this(false, changeType, changeNumber); 207 } 208 209 210 211 /** 212 * Creates a new entry change notification control with the provided 213 * information. 214 * 215 * @param changeType The change type for this change notification control. 216 * @param previousDN The DN that the entry had prior to a modify DN 217 * operation, or <CODE>null</CODE> if the operation was 218 * not a modify DN. 219 * @param changeNumber The change number for the associated change, or a 220 * negative value if no change number is available. 221 */ 222 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 223 DN previousDN, long changeNumber) 224 { 225 this(false, changeType, previousDN, changeNumber); 226 } 227 228 229 230 /** 231 * Writes this control value to an ASN.1 writer. The value (if any) must be 232 * written as an ASN1OctetString. 233 * 234 * @param writer The ASN.1 output stream to write to. 235 * @throws IOException If a problem occurs while writing to the stream. 236 */ 237 public void writeValue(ASN1Writer writer) throws IOException { 238 writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE); 239 240 writer.writeStartSequence(); 241 writer.writeEnumerated(changeType.intValue()); 242 243 if (previousDN != null) 244 { 245 writer.writeOctetString(previousDN.toString()); 246 } 247 248 if (changeNumber > 0) 249 { 250 writer.writeInteger(changeNumber); 251 } 252 writer.writeEndSequence(); 253 254 writer.writeEndSequence(); 255 } 256 257 258 259 /** 260 * Retrieves the change type for this entry change notification control. 261 * 262 * @return The change type for this entry change notification control. 263 */ 264 public PersistentSearchChangeType getChangeType() 265 { 266 return changeType; 267 } 268 269 270 /** 271 * Retrieves the previous DN for this entry change notification control. 272 * 273 * @return The previous DN for this entry change notification control, or 274 * <CODE>null</CODE> if there is none. 275 */ 276 public DN getPreviousDN() 277 { 278 return previousDN; 279 } 280 281 282 283 /** 284 * Retrieves the change number for this entry change notification control. 285 * 286 * @return The change number for this entry change notification control, or a 287 * negative value if no change number is available. 288 */ 289 public long getChangeNumber() 290 { 291 return changeNumber; 292 } 293 294 295 296 /** 297 * Appends a string representation of this entry change notification control 298 * to the provided buffer. 299 * 300 * @param buffer The buffer to which the information should be appended. 301 */ 302 public void toString(StringBuilder buffer) 303 { 304 buffer.append("EntryChangeNotificationControl(changeType="); 305 buffer.append(changeType); 306 307 if (previousDN != null) 308 { 309 buffer.append(",previousDN=\"").append(previousDN).append("\""); 310 } 311 312 if (changeNumber > 0) 313 { 314 buffer.append(",changeNumber="); 315 buffer.append(changeNumber); 316 } 317 318 buffer.append(")"); 319 } 320} 321