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}