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 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.replication.common;
028
029import java.io.Serializable;
030import java.util.Date;
031
032import org.forgerock.opendj.ldap.ByteSequence;
033import org.forgerock.opendj.ldap.ByteSequenceReader;
034import org.forgerock.opendj.ldap.ByteString;
035import org.forgerock.opendj.ldap.ByteStringBuilder;
036
037/**
038 * Class used to represent Change Sequence Numbers.
039 *
040 * @see <a href="http://tools.ietf.org/html/draft-ietf-ldup-infomod-08"
041 * >Inspiration for this class comes from LDAPChangeSequenceNumber</a>
042 */
043public class CSN implements Serializable, Comparable<CSN>
044{
045  /**
046   * The number of bytes used by the byte string representation of a change
047   * number.
048   *
049   * @see #valueOf(ByteSequence)
050   * @see #toByteString()
051   * @see #toByteString(ByteStringBuilder)
052   */
053  public static final int BYTE_ENCODING_LENGTH = 14;
054
055  /**
056   * The number of characters used by the string representation of a change
057   * number.
058   *
059   * @see #valueOf(String)
060   * @see #toString()
061   */
062  public static final int STRING_ENCODING_LENGTH = 28;
063
064  /** The maximum possible value for a CSN. */
065  public static final CSN MAX_CSN_VALUE = new CSN(Long.MAX_VALUE, Integer.MAX_VALUE, Short.MAX_VALUE);
066
067  private static final long serialVersionUID = -8802722277749190740L;
068  private final long timeStamp;
069  /**
070   * The sequence number is set to zero at the start of each millisecond, and
071   * incremented by one for each update operation that occurs within that
072   * millisecond. It allows to distinguish changes that have been done in the
073   * same millisecond.
074   */
075  private final int seqnum;
076  private final int serverId;
077
078  /**
079   * Parses the provided {@link #toString()} representation of a CSN.
080   *
081   * @param s
082   *          The string to be parsed.
083   * @return The parsed CSN.
084   * @see #toString()
085   */
086  public static CSN valueOf(String s)
087  {
088    return new CSN(s);
089  }
090
091  /**
092   * Decodes the provided {@link #toByteString()} representation of a change
093   * number.
094   *
095   * @param bs
096   *          The byte sequence to be parsed.
097   * @return The decoded CSN.
098   * @see #toByteString()
099   */
100  public static CSN valueOf(ByteSequence bs)
101  {
102    ByteSequenceReader reader = bs.asReader();
103    long timeStamp = reader.getLong();
104    int serverId = reader.getShort() & 0xffff;
105    int seqnum = reader.getInt();
106    return new CSN(timeStamp, seqnum, serverId);
107  }
108
109  /**
110   * Create a new {@link CSN} from a String.
111   *
112   * @param str
113   *          the string from which to create a {@link CSN}
114   */
115  public CSN(String str)
116  {
117    String temp = str.substring(0, 16);
118    timeStamp = Long.parseLong(temp, 16);
119
120    temp = str.substring(16, 20);
121    serverId = Integer.parseInt(temp, 16);
122
123    temp = str.substring(20, 28);
124    seqnum = Integer.parseInt(temp, 16);
125  }
126
127  /**
128   * Create a new {@link CSN}.
129   *
130   * @param timeStamp
131   *          timeStamp for the {@link CSN}
132   * @param seqNum
133   *          sequence number
134   * @param serverId
135   *          identity of server
136   */
137  public CSN(long timeStamp, int seqNum, int serverId)
138  {
139    this.serverId = serverId;
140    this.timeStamp = timeStamp;
141    this.seqnum = seqNum;
142  }
143
144  /**
145   * Getter for the time.
146   *
147   * @return the time
148   */
149  public long getTime()
150  {
151    return timeStamp;
152  }
153
154  /**
155   * Get the timestamp associated to this {@link CSN} in seconds.
156   *
157   * @return timestamp associated to this {@link CSN} in seconds
158   */
159  public long getTimeSec()
160  {
161    return timeStamp / 1000;
162  }
163
164  /**
165   * Getter for the sequence number.
166   *
167   * @return the sequence number
168   */
169  public int getSeqnum()
170  {
171    return seqnum;
172  }
173
174  /**
175   * Getter for the server ID.
176   *
177   * @return the server ID
178   */
179  public int getServerId()
180  {
181    return serverId;
182  }
183
184  /** {@inheritDoc} */
185  @Override
186  public boolean equals(Object obj)
187  {
188    if (this == obj)
189    {
190      return true;
191    }
192    else if (obj instanceof CSN)
193    {
194      final CSN csn = (CSN) obj;
195      return this.seqnum == csn.seqnum && this.serverId == csn.serverId
196          && this.timeStamp == csn.timeStamp;
197    }
198    else
199    {
200      return false;
201    }
202  }
203
204  /** {@inheritDoc} */
205  @Override
206  public int hashCode()
207  {
208    return this.seqnum + this.serverId + Long.valueOf(timeStamp).hashCode();
209  }
210
211  /**
212   * Encodes this CSN as a byte string.
213   * <p>
214   * NOTE: this representation must not be modified otherwise interop with
215   * earlier protocol versions will be broken.
216   *
217   * @return The encoded representation of this CSN.
218   * @see #valueOf(ByteSequence)
219   */
220  public ByteString toByteString()
221  {
222    return toByteString(new ByteStringBuilder(BYTE_ENCODING_LENGTH))
223        .toByteString();
224  }
225
226  /**
227   * Encodes this CSN into the provided byte string builder.
228   * <p>
229   * NOTE: this representation must not be modified otherwise interop with
230   * earlier protocol versions will be broken.
231   *
232   * @param builder
233   *          The byte string builder.
234   * @return The byte string builder containing the encoded CSN.
235   * @see #valueOf(ByteSequence)
236   */
237  public ByteStringBuilder toByteString(ByteStringBuilder builder)
238  {
239    return builder.append(timeStamp).append((short) (serverId & 0xffff))
240        .append(seqnum);
241  }
242
243  /**
244   * Convert the {@link CSN} to a printable String.
245   * <p>
246   * NOTE: this representation must not be modified otherwise interop with
247   * earlier protocol versions will be broken.
248   *
249   * @return the string
250   */
251  @Override
252  public String toString()
253  {
254    return String.format("%016x%04x%08x", timeStamp, serverId, seqnum);
255  }
256
257  /**
258   * Convert the {@link CSN} to a printable String with a user friendly format.
259   *
260   * @return the string
261   */
262  public String toStringUI()
263  {
264    Date date = new Date(timeStamp);
265    return String.format(
266        "%016x%04x%08x (sid=%d,tsd=%s,ts=%d,seqnum=%d)",
267        timeStamp, serverId, seqnum,
268        serverId, date.toString(), timeStamp, seqnum);
269  }
270
271  /**
272   * Compares this CSN with the provided CSN for order and returns a negative
273   * number if {@code csn1} is older than {@code csn2}, zero if they have the
274   * same age, or a positive number if {@code csn1} is newer than {@code csn2}.
275   *
276   * @param csn1
277   *          The first CSN to be compared, which may be {@code null}.
278   * @param csn2
279   *          The second CSN to be compared, which may be {@code null}.
280   * @return A negative number if {@code csn1} is older than {@code csn2}, zero
281   *         if they have the same age, or a positive number if {@code csn1} is
282   *         newer than {@code csn2}.
283   */
284  public static int compare(CSN csn1, CSN csn2)
285  {
286    if (csn1 == null)
287    {
288      return csn2 == null ? 0 : -1;
289    }
290    else if (csn2 == null)
291    {
292      return 1;
293    }
294    else if (csn1.timeStamp != csn2.timeStamp)
295    {
296      return csn1.timeStamp < csn2.timeStamp ? -1 : 1;
297    }
298    else if (csn1.seqnum != csn2.seqnum)
299    {
300      return csn1.seqnum - csn2.seqnum;
301    }
302    else
303    {
304      return csn1.serverId - csn2.serverId;
305    }
306  }
307
308  /**
309   * Computes the difference in number of changes between 2 CSNs. First one is
310   * expected to be newer than second one. If this is not the case, 0 will be
311   * returned.
312   *
313   * @param csn1
314   *          the first {@link CSN}
315   * @param csn2
316   *          the second {@link CSN}
317   * @return the difference
318   */
319  public static int diffSeqNum(CSN csn1, CSN csn2)
320  {
321    if (csn1 == null)
322    {
323      return 0;
324    }
325    if (csn2 == null)
326    {
327      return csn1.getSeqnum();
328    }
329    if (csn2.isNewerThanOrEqualTo(csn1))
330    {
331      return 0;
332    }
333
334    int seqnum1 = csn1.getSeqnum();
335    long time1 = csn1.getTime();
336    int seqnum2 = csn2.getSeqnum();
337    long time2 = csn2.getTime();
338
339    if (time2 <= time1)
340    {
341      if (seqnum2 <= seqnum1)
342      {
343        return seqnum1 - seqnum2;
344      }
345      return Integer.MAX_VALUE - (seqnum2 - seqnum1) + 1;
346    }
347    return 0;
348  }
349
350  /**
351   * Returns {@code true} if this CSN is older than the provided CSN.
352   *
353   * @param csn
354   *          The CSN to be compared.
355   * @return {@code true} if this CSN is older than the provided CSN.
356   */
357  public boolean isOlderThan(CSN csn)
358  {
359    return compare(this, csn) < 0;
360  }
361
362  /**
363   * Returns {@code true} if this CSN is older than or equal to the provided
364   * CSN.
365   *
366   * @param csn
367   *          The CSN to be compared.
368   * @return {@code true} if this CSN is older than or equal to the provided
369   *         CSN.
370   */
371  public boolean isOlderThanOrEqualTo(CSN csn)
372  {
373    return compare(this, csn) <= 0;
374  }
375
376  /**
377   * Returns {@code true} if this CSN is newer than or equal to the provided
378   * CSN.
379   *
380   * @param csn
381   *          The CSN to be compared.
382   * @return {@code true} if this CSN is newer than or equal to the provided
383   *         CSN.
384   */
385  public boolean isNewerThanOrEqualTo(CSN csn)
386  {
387    return compare(this, csn) >= 0;
388  }
389
390  /**
391   * Returns {@code true} if this CSN is newer than the provided CSN.
392   *
393   * @param csn
394   *          The CSN to be compared.
395   * @return {@code true} if this CSN is newer than the provided CSN.
396   */
397  public boolean isNewerThan(CSN csn)
398  {
399    return compare(this, csn) > 0;
400  }
401
402  /**
403   * Compares this CSN with the provided CSN for order and returns a negative
404   * number if this CSN is older than {@code csn}, zero if they have the same
405   * age, or a positive number if this CSN is newer than {@code csn}.
406   *
407   * @param csn
408   *          The CSN to be compared.
409   * @return A negative number if this CSN is older than {@code csn}, zero if
410   *         they have the same age, or a positive number if this CSN is newer
411   *         than {@code csn}.
412   */
413  @Override
414  public int compareTo(CSN csn)
415  {
416    return compare(this, csn);
417  }
418}