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