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.readLong(); 104 int serverId = reader.readShort() & 0xffff; 105 int seqnum = reader.readInt(); 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 @Override 185 public boolean equals(Object obj) 186 { 187 if (this == obj) 188 { 189 return true; 190 } 191 else if (obj instanceof CSN) 192 { 193 final CSN csn = (CSN) obj; 194 return this.seqnum == csn.seqnum && this.serverId == csn.serverId 195 && this.timeStamp == csn.timeStamp; 196 } 197 else 198 { 199 return false; 200 } 201 } 202 203 @Override 204 public int hashCode() 205 { 206 return this.seqnum + this.serverId + Long.valueOf(timeStamp).hashCode(); 207 } 208 209 /** 210 * Encodes this CSN as a byte string. 211 * <p> 212 * NOTE: this representation must not be modified otherwise interop with 213 * earlier protocol versions will be broken. 214 * 215 * @return The encoded representation of this CSN. 216 * @see #valueOf(ByteSequence) 217 */ 218 public ByteString toByteString() 219 { 220 final ByteStringBuilder builder = new ByteStringBuilder(BYTE_ENCODING_LENGTH); 221 toByteString(builder); 222 return builder.toByteString(); 223 } 224 225 /** 226 * Encodes this CSN into the provided byte string builder. 227 * <p> 228 * NOTE: this representation must not be modified otherwise interop with 229 * earlier protocol versions will be broken. 230 * 231 * @param builder 232 * The byte string builder. 233 * @see #valueOf(ByteSequence) 234 */ 235 public void toByteString(ByteStringBuilder builder) 236 { 237 builder.appendLong(timeStamp).appendShort(serverId & 0xffff).appendInt(seqnum); 238 } 239 240 /** 241 * Convert the {@link CSN} to a printable String. 242 * <p> 243 * NOTE: this representation must not be modified otherwise interop with 244 * earlier protocol versions will be broken. 245 * 246 * @return the string 247 */ 248 @Override 249 public String toString() 250 { 251 final StringBuilder buffer = new StringBuilder(); 252 toString(buffer); 253 return buffer.toString(); 254 } 255 256 /** 257 * Appends the text representation of this {@link CSN} into the provided StringBuilder. 258 * <p> 259 * NOTE: this representation must not be modified otherwise interop with 260 * earlier protocol versions will be broken. 261 * 262 * @param buffer the StringBuilder where to output the CSN text representation 263 */ 264 void toString(final StringBuilder buffer) 265 { 266 leftPadWithZeros(buffer, 16, Long.toHexString(timeStamp)); 267 leftPadWithZeros(buffer, 4, Integer.toHexString(serverId)); 268 leftPadWithZeros(buffer, 8, Integer.toHexString(seqnum)); 269 } 270 271 private void leftPadWithZeros(StringBuilder buffer, int nbChars, String toAppend) 272 { 273 final int padding = nbChars - toAppend.length(); 274 for (int i = 0; i < padding; i++) 275 { 276 buffer.append('0'); 277 } 278 buffer.append(toAppend); 279 } 280 281 /** 282 * Convert the {@link CSN} to a printable String with a user friendly format. 283 * 284 * @return the string 285 */ 286 public String toStringUI() 287 { 288 final StringBuilder buffer = new StringBuilder(); 289 toStringUI(buffer); 290 return buffer.toString(); 291 } 292 293 private void toStringUI(final StringBuilder buffer) 294 { 295 toString(buffer); 296 buffer.append(" (sid=").append(serverId) 297 .append(",tsd=").append(new Date(timeStamp)) 298 .append(",ts=").append(timeStamp) 299 .append(",seqnum=").append(seqnum) 300 .append(")"); 301 } 302 303 /** 304 * Compares this CSN with the provided CSN for order and returns a negative 305 * number if {@code csn1} is older than {@code csn2}, zero if they have the 306 * same age, or a positive number if {@code csn1} is newer than {@code csn2}. 307 * 308 * @param csn1 309 * The first CSN to be compared, which may be {@code null}. 310 * @param csn2 311 * The second CSN to be compared, which may be {@code null}. 312 * @return A negative number if {@code csn1} is older than {@code csn2}, zero 313 * if they have the same age, or a positive number if {@code csn1} is 314 * newer than {@code csn2}. 315 */ 316 public static int compare(CSN csn1, CSN csn2) 317 { 318 if (csn1 == null) 319 { 320 return csn2 == null ? 0 : -1; 321 } 322 else if (csn2 == null) 323 { 324 return 1; 325 } 326 else if (csn1.timeStamp != csn2.timeStamp) 327 { 328 return csn1.timeStamp < csn2.timeStamp ? -1 : 1; 329 } 330 else if (csn1.seqnum != csn2.seqnum) 331 { 332 return csn1.seqnum - csn2.seqnum; 333 } 334 else 335 { 336 return csn1.serverId - csn2.serverId; 337 } 338 } 339 340 /** 341 * Computes the difference in number of changes between 2 CSNs. First one is 342 * expected to be newer than second one. If this is not the case, 0 will be 343 * returned. 344 * 345 * @param csn1 346 * the first {@link CSN} 347 * @param csn2 348 * the second {@link CSN} 349 * @return the difference 350 */ 351 public static int diffSeqNum(CSN csn1, CSN csn2) 352 { 353 if (csn1 == null) 354 { 355 return 0; 356 } 357 if (csn2 == null) 358 { 359 return csn1.getSeqnum(); 360 } 361 if (csn2.isNewerThanOrEqualTo(csn1)) 362 { 363 return 0; 364 } 365 366 int seqnum1 = csn1.getSeqnum(); 367 long time1 = csn1.getTime(); 368 int seqnum2 = csn2.getSeqnum(); 369 long time2 = csn2.getTime(); 370 371 if (time2 <= time1) 372 { 373 if (seqnum2 <= seqnum1) 374 { 375 return seqnum1 - seqnum2; 376 } 377 return Integer.MAX_VALUE - (seqnum2 - seqnum1) + 1; 378 } 379 return 0; 380 } 381 382 /** 383 * Returns {@code true} if this CSN is older than the provided CSN. 384 * 385 * @param csn 386 * The CSN to be compared. 387 * @return {@code true} if this CSN is older than the provided CSN. 388 */ 389 public boolean isOlderThan(CSN csn) 390 { 391 return compare(this, csn) < 0; 392 } 393 394 /** 395 * Returns {@code true} if this CSN is older than or equal to the provided 396 * CSN. 397 * 398 * @param csn 399 * The CSN to be compared. 400 * @return {@code true} if this CSN is older than or equal to the provided 401 * CSN. 402 */ 403 public boolean isOlderThanOrEqualTo(CSN csn) 404 { 405 return compare(this, csn) <= 0; 406 } 407 408 /** 409 * Returns {@code true} if this CSN is newer than or equal to the provided 410 * CSN. 411 * 412 * @param csn 413 * The CSN to be compared. 414 * @return {@code true} if this CSN is newer than or equal to the provided 415 * CSN. 416 */ 417 public boolean isNewerThanOrEqualTo(CSN csn) 418 { 419 return compare(this, csn) >= 0; 420 } 421 422 /** 423 * Returns {@code true} if this CSN is newer than the provided CSN. 424 * 425 * @param csn 426 * The CSN to be compared. 427 * @return {@code true} if this CSN is newer than the provided CSN. 428 */ 429 public boolean isNewerThan(CSN csn) 430 { 431 return compare(this, csn) > 0; 432 } 433 434 /** 435 * Compares this CSN with the provided CSN for order and returns a negative 436 * number if this CSN is older than {@code csn}, zero if they have the same 437 * age, or a positive number if this CSN is newer than {@code csn}. 438 * 439 * @param csn 440 * The CSN to be compared. 441 * @return A negative number if this CSN is older than {@code csn}, zero if 442 * they have the same age, or a positive number if this CSN is newer 443 * than {@code csn}. 444 */ 445 @Override 446 public int compareTo(CSN csn) 447 { 448 return compare(this, csn); 449 } 450}