/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. */ package org.opends.server.controls; import java.util.ArrayList; import org.opends.server.protocols.asn1.ASN1Constants; import org.opends.server.protocols.asn1.ASN1Element; import org.opends.server.protocols.asn1.ASN1Enumerated; import org.opends.server.protocols.asn1.ASN1Long; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.protocols.asn1.ASN1Sequence; import org.opends.server.protocols.ldap.LDAPException; import org.opends.server.protocols.ldap.LDAPResultCode; import org.opends.server.types.Control; import org.opends.server.types.DN; import static org.opends.server.loggers.Debug.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.messages.ProtocolMessages.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; /** * This class implements the entry change notification control defined in * draft-ietf-ldapext-psearch. It may be included in entries returned in * response to a persistent search operation. */ public class EntryChangeNotificationControl extends Control { /** * The fully-qualified name of this class for debugging purposes. */ private static final String CLASS_NAME = "org.opends.server.controls.EntryChangeNotificationControl"; // The previous DN for this change notification control. private DN previousDN; // The change number for this change notification control. private long changeNumber; // The change type for this change notification control. private PersistentSearchChangeType changeType; /** * Creates a new entry change notification control with the provided * information. * * @param changeType The change type for this change notification control. * @param changeNumber The change number for the associated change, or a * negative value if no change number is available. */ public EntryChangeNotificationControl(PersistentSearchChangeType changeType, long changeNumber) { super(OID_ENTRY_CHANGE_NOTIFICATION, false, encodeValue(changeType, null, changeNumber)); assert debugConstructor(CLASS_NAME, String.valueOf(changeType), String.valueOf(changeNumber)); this.changeType = changeType; this.changeNumber = changeNumber; previousDN = null; } /** * Creates a new entry change notification control with the provided * information. * * @param changeType The change type for this change notification control. * @param previousDN The DN that the entry had prior to a modify DN * operation, or null if the operation was * not a modify DN. * @param changeNumber The change number for the associated change, or a * negative value if no change number is available. */ public EntryChangeNotificationControl(PersistentSearchChangeType changeType, DN previousDN, long changeNumber) { super(OID_ENTRY_CHANGE_NOTIFICATION, false, encodeValue(changeType, previousDN, changeNumber)); assert debugConstructor(CLASS_NAME, String.valueOf(changeType), String.valueOf(previousDN), String.valueOf(changeNumber)); this.changeType = changeType; this.previousDN = previousDN; this.changeNumber = changeNumber; } /** * Creates a new entry change notification control with the provided * information. * * @param oid The OID to use for this control. * @param isCritical Indicates whether this control should be considered * critical to the operation processing. * @param changeType The change type for this change notification control. * @param previousDN The DN that the entry had prior to a modify DN * operation, or null if the operation was * not a modify DN. * @param changeNumber The change number for the associated change, or a * negative value if no change number is available. */ public EntryChangeNotificationControl(String oid, boolean isCritical, PersistentSearchChangeType changeType, DN previousDN, long changeNumber) { super(oid, isCritical, encodeValue(changeType, previousDN, changeNumber)); assert debugConstructor(CLASS_NAME, String.valueOf(oid), String.valueOf(isCritical), String.valueOf(changeType), String.valueOf(previousDN), String.valueOf(changeNumber)); this.changeType = changeType; this.previousDN = previousDN; this.changeNumber = changeNumber; } /** * Creates a new entry change notification control with the provided * information. * * @param oid The OID to use for this control. * @param isCritical Indicates whether this control should be considered * critical to the operation processing. * @param changeType The change type for this change notification control. * @param previousDN The DN that the entry had prior to a modify DN * operation, or null if the operation was * not a modify DN. * @param changeNumber The change number for the associated change, or a * negative value if no change number is available. * @param encodedValue The pre-encoded value for this change notification * control. */ private EntryChangeNotificationControl(String oid, boolean isCritical, PersistentSearchChangeType changeType, DN previousDN, long changeNumber, ASN1OctetString encodedValue) { super(oid, isCritical, encodedValue); assert debugConstructor(CLASS_NAME, String.valueOf(oid), String.valueOf(isCritical), String.valueOf(changeType), String.valueOf(previousDN), String.valueOf(changeNumber), String.valueOf(encodedValue)); this.changeType = changeType; this.previousDN = previousDN; this.changeNumber = changeNumber; } /** * Encodes the provided information into an ASN.1 octet string suitable for * use as the control value. * * @param changeType The change type for this change notification control. * @param previousDN The DN that the entry had prior to a modify DN * operation, or null if the operation was * not a modify DN. * @param changeNumber The change number for the associated change, or a * negative value if no change number is available. * * @return An ASN.1 octet string containing the encoded information. */ private static ASN1OctetString encodeValue(PersistentSearchChangeType changeType, DN previousDN, long changeNumber) { assert debugEnter(CLASS_NAME, "encodeValue", String.valueOf(changeType), String.valueOf(previousDN), String.valueOf(changeNumber)); ArrayList elements = new ArrayList(3); elements.add(new ASN1Enumerated(changeType.intValue())); if (previousDN != null) { elements.add(new ASN1OctetString(previousDN.toString())); } if (changeNumber > 0) { elements.add(new ASN1Long(changeNumber)); } return new ASN1OctetString(new ASN1Sequence(elements).encode()); } /** * Creates a new entry change notification control from the contents of the * provided control. * * @param control The generic control containing the information to use to * create this entry change notification control. * * @return The entry change notification control decoded from the provided * control. * * @throws LDAPException If this control cannot be decoded as a valid * entry change notification control. */ public static EntryChangeNotificationControl decodeControl(Control control) throws LDAPException { assert debugEnter(CLASS_NAME, "decodeControl", String.valueOf(control)); if (! control.hasValue()) { int msgID = MSGID_ECN_NO_CONTROL_VALUE; String message = getMessage(msgID); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message); } DN previousDN = null; long changeNumber = -1; PersistentSearchChangeType changeType; try { ArrayList elements = ASN1Sequence.decodeAsSequence(control.getValue().value()).elements(); if ((elements.size() < 1) || (elements.size() > 3)) { int msgID = MSGID_ECN_INVALID_ELEMENT_COUNT; String message = getMessage(msgID, elements.size()); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message); } int changeTypeValue = elements.get(0).decodeAsEnumerated().intValue(); changeType = PersistentSearchChangeType.valueOf(changeTypeValue); if (elements.size() == 2) { ASN1Element e = elements.get(1); if (e.getType() == ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE) { if (changeType != PersistentSearchChangeType.MODIFY_DN) { int msgID = MSGID_ECN_ILLEGAL_PREVIOUS_DN; String message = getMessage(msgID, String.valueOf(changeType)); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message); } ASN1OctetString rawPreviousDN = e.decodeAsOctetString(); previousDN = DN.decode(rawPreviousDN); } else if (e.getType() == ASN1Constants.UNIVERSAL_INTEGER_TYPE) { changeNumber = e.decodeAsLong().longValue(); } else { int msgID = MSGID_ECN_INVALID_ELEMENT_TYPE; String message = getMessage(msgID, byteToHex(e.getType())); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message); } } else if (elements.size() == 3) { if (changeType != PersistentSearchChangeType.MODIFY_DN) { int msgID = MSGID_ECN_ILLEGAL_PREVIOUS_DN; String message = getMessage(msgID, String.valueOf(changeType)); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message); } ASN1OctetString rawPreviousDN = elements.get(1).decodeAsOctetString(); previousDN = DN.decode(rawPreviousDN); changeNumber = elements.get(2).decodeAsLong().longValue(); } } catch (LDAPException le) { throw le; } catch (Exception e) { assert debugException(CLASS_NAME, "decodeControl", e); int msgID = MSGID_ECN_CANNOT_DECODE_VALUE; String message = getMessage(msgID, stackTraceToSingleLineString(e)); throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message, e); } return new EntryChangeNotificationControl(control.getOID(), control.isCritical(), changeType, previousDN, changeNumber, control.getValue()); } /** * Retrieves the change type for this entry change notification control. * * @return The change type for this entry change notification control. */ public PersistentSearchChangeType getChangeType() { assert debugEnter(CLASS_NAME, "getChangeType"); return changeType; } /** * Sets the change type for this entry change notification control. * * @param changeType The change type for this entry change notification * control. */ public void setChangeType(PersistentSearchChangeType changeType) { assert debugEnter(CLASS_NAME, "setChangeType", String.valueOf(changeType)); this.changeType = changeType; setValue(encodeValue(changeType, previousDN, changeNumber)); } /** * Retrieves the previous DN for this entry change notification control. * * @return The previous DN for this entry change notification control, or * null if there is none. */ public DN getPreviousDN() { assert debugEnter(CLASS_NAME, "getPreviousDN"); return previousDN; } /** * Specifies the previous DN for this entry change notification control. * * @param previousDN The previous DN for this entry change notification * control. */ public void setPreviousDN(DN previousDN) { assert debugEnter(CLASS_NAME, "setPreviousDN", String.valueOf(previousDN)); this.previousDN = previousDN; setValue(encodeValue(changeType, previousDN, changeNumber)); } /** * Retrieves the change number for this entry change notification control. * * @return The change number for this entry change notification control, or a * negative value if no change number is available. */ public long getChangeNumber() { assert debugEnter(CLASS_NAME, "getChangeNumber"); return changeNumber; } /** * Specifies the change number for this entry change notification control. * * @param changeNumber The change number for this entry change notification * control. */ public void setChangeNumber(long changeNumber) { assert debugEnter(CLASS_NAME, "setChangeNumber", String.valueOf(changeNumber)); this.changeNumber = changeNumber; setValue(encodeValue(changeType, previousDN, changeNumber)); } /** * Retrieves a string representation of this entry change notification * control. * * @return A string representation of this entry change notification control. */ public String toString() { assert debugEnter(CLASS_NAME, "toString"); StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } /** * Appends a string representation of this entry change notification control * to the provided buffer. * * @param buffer The buffer to which the information should be appended. */ public void toString(StringBuilder buffer) { assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder"); buffer.append("EntryChangeNotificationControl(changeType="); buffer.append(changeType.toString()); if (previousDN != null) { buffer.append(",previousDN=\""); buffer.append(previousDN.toString()); buffer.append("\""); } if (changeNumber > 0) { buffer.append(",changeNumber="); buffer.append(changeNumber); } buffer.append(")"); } }