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