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