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