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; 018 019import static org.opends.messages.ProtocolMessages.*; 020import static org.opends.server.util.ServerConstants.*; 021import static org.opends.server.util.StaticUtils.*; 022 023import java.io.IOException; 024 025import org.forgerock.i18n.LocalizableMessage; 026import org.forgerock.i18n.slf4j.LocalizedLogger; 027import org.forgerock.opendj.io.ASN1; 028import org.forgerock.opendj.io.ASN1Reader; 029import org.forgerock.opendj.io.ASN1Writer; 030import org.forgerock.opendj.ldap.ByteString; 031import org.forgerock.opendj.ldap.DN; 032import org.forgerock.opendj.ldap.ResultCode; 033import org.opends.server.types.Control; 034import org.opends.server.types.DirectoryException; 035 036/** 037 * This class implements the entry change notification control defined in 038 * draft-ietf-ldapext-psearch. It may be included in entries returned in 039 * response to a persistent search operation. 040 */ 041public class EntryChangeNotificationControl 042 extends Control 043{ 044 /** ControlDecoder implementation to decode this control from a ByteString. */ 045 private static final class Decoder 046 implements ControlDecoder<EntryChangeNotificationControl> 047 { 048 @Override 049 public EntryChangeNotificationControl decode( 050 boolean isCritical, ByteString value) throws DirectoryException 051 { 052 if (value == null) 053 { 054 LocalizableMessage message = ERR_ECN_NO_CONTROL_VALUE.get(); 055 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 056 } 057 058 059 DN previousDN = null; 060 long changeNumber = -1; 061 PersistentSearchChangeType changeType; 062 ASN1Reader reader = ASN1.getReader(value); 063 try 064 { 065 reader.readStartSequence(); 066 067 int changeTypeValue = (int)reader.readInteger(); 068 changeType = PersistentSearchChangeType.valueOf(changeTypeValue); 069 070 if(reader.hasNextElement() && 071 reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE) 072 { 073 if (changeType != PersistentSearchChangeType.MODIFY_DN) 074 { 075 LocalizableMessage message = ERR_ECN_ILLEGAL_PREVIOUS_DN.get(changeType); 076 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 077 } 078 079 previousDN = DN.valueOf(reader.readOctetStringAsString()); 080 } 081 if(reader.hasNextElement() && 082 reader.peekType() == ASN1.UNIVERSAL_INTEGER_TYPE) 083 { 084 changeNumber = reader.readInteger(); 085 } 086 } 087 catch (DirectoryException de) 088 { 089 throw de; 090 } 091 catch (Exception e) 092 { 093 logger.traceException(e); 094 095 LocalizableMessage message = 096 ERR_ECN_CANNOT_DECODE_VALUE.get(getExceptionMessage(e)); 097 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e); 098 } 099 100 101 return new EntryChangeNotificationControl(isCritical, changeType, 102 previousDN, changeNumber); 103 } 104 105 @Override 106 public String getOID() 107 { 108 return OID_ENTRY_CHANGE_NOTIFICATION; 109 } 110 111 } 112 113 /** The ControlDecoder that can be used to decode this control. */ 114 public static final ControlDecoder<EntryChangeNotificationControl> DECODER = 115 new Decoder(); 116 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 117 118 119 120 121 /** The previous DN for this change notification control. */ 122 private DN previousDN; 123 124 /** The change number for this change notification control. */ 125 private long changeNumber; 126 127 /** The change type for this change notification control. */ 128 private PersistentSearchChangeType changeType; 129 130 131 /** 132 * Creates a new entry change notification control with the provided 133 * information. 134 * 135 * @param isCritical Indicates whether this control should be 136 * considered critical in processing the 137 * request. 138 * @param changeType The change type for this change notification control. 139 * @param changeNumber The change number for the associated change, or a 140 * negative value if no change number is available. 141 */ 142 public EntryChangeNotificationControl(boolean isCritical, 143 PersistentSearchChangeType changeType, 144 long changeNumber) 145 { 146 super(OID_ENTRY_CHANGE_NOTIFICATION, isCritical); 147 148 149 this.changeType = changeType; 150 this.changeNumber = changeNumber; 151 152 previousDN = null; 153 } 154 155 156 157 /** 158 * Creates a new entry change notification control with the provided 159 * information. 160 * 161 * @param isCritical Indicates whether this control should be 162 * considered critical in processing the 163 * request. 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 */ 171 public EntryChangeNotificationControl(boolean isCritical, 172 PersistentSearchChangeType changeType, 173 DN previousDN, long changeNumber) 174 { 175 super(OID_ENTRY_CHANGE_NOTIFICATION, isCritical); 176 177 178 this.changeType = changeType; 179 this.previousDN = previousDN; 180 this.changeNumber = changeNumber; 181 } 182 183 184 /** 185 * Creates a new entry change notification control with the provided 186 * information. 187 * 188 * @param changeType The change type for this change notification control. 189 * @param changeNumber The change number for the associated change, or a 190 * negative value if no change number is available. 191 */ 192 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 193 long changeNumber) 194 { 195 this(false, changeType, changeNumber); 196 } 197 198 199 200 /** 201 * Creates a new entry change notification control with the provided 202 * information. 203 * 204 * @param changeType The change type for this change notification control. 205 * @param previousDN The DN that the entry had prior to a modify DN 206 * operation, or <CODE>null</CODE> if the operation was 207 * not a modify DN. 208 * @param changeNumber The change number for the associated change, or a 209 * negative value if no change number is available. 210 */ 211 public EntryChangeNotificationControl(PersistentSearchChangeType changeType, 212 DN previousDN, long changeNumber) 213 { 214 this(false, changeType, previousDN, changeNumber); 215 } 216 217 @Override 218 public void writeValue(ASN1Writer writer) throws IOException { 219 writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE); 220 221 writer.writeStartSequence(); 222 writer.writeEnumerated(changeType.intValue()); 223 224 if (previousDN != null) 225 { 226 writer.writeOctetString(previousDN.toString()); 227 } 228 229 if (changeNumber > 0) 230 { 231 writer.writeInteger(changeNumber); 232 } 233 writer.writeEndSequence(); 234 235 writer.writeEndSequence(); 236 } 237 238 239 240 /** 241 * Retrieves the change type for this entry change notification control. 242 * 243 * @return The change type for this entry change notification control. 244 */ 245 public PersistentSearchChangeType getChangeType() 246 { 247 return changeType; 248 } 249 250 251 /** 252 * Retrieves the previous DN for this entry change notification control. 253 * 254 * @return The previous DN for this entry change notification control, or 255 * <CODE>null</CODE> if there is none. 256 */ 257 public DN getPreviousDN() 258 { 259 return previousDN; 260 } 261 262 263 264 /** 265 * Retrieves the change number for this entry change notification control. 266 * 267 * @return The change number for this entry change notification control, or a 268 * negative value if no change number is available. 269 */ 270 public long getChangeNumber() 271 { 272 return changeNumber; 273 } 274 275 @Override 276 public void toString(StringBuilder buffer) 277 { 278 buffer.append("EntryChangeNotificationControl(changeType="); 279 buffer.append(changeType); 280 281 if (previousDN != null) 282 { 283 buffer.append(",previousDN=\"").append(previousDN).append("\""); 284 } 285 286 if (changeNumber > 0) 287 { 288 buffer.append(",changeNumber="); 289 buffer.append(changeNumber); 290 } 291 292 buffer.append(")"); 293 } 294}