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