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}