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