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 2011-2014 ForgeRock AS 026 */ 027package org.opends.server.replication.common; 028 029import org.opends.server.util.TimeThread; 030 031/** 032 * This class defines a structure that is used for storing the last {@link CSN}s 033 * generated on this server or received from other servers and generating new 034 * {@link CSN}s that are guaranteed to be larger than all the previously seen or 035 * generated CSNs. 036 */ 037public class CSNGenerator 038{ 039 private long lastTime; 040 /** 041 * The sequence number allows to distinguish changes that have been done in 042 * the same millisecond. 043 * 044 * @see #lastTime 045 */ 046 private int seqnum; 047 private final int serverId; 048 049 /** 050 * Create a new {@link CSNGenerator}. 051 * 052 * @param serverId 053 * id to use when creating {@link CSN}s. 054 * @param timestamp 055 * time to start with. 056 */ 057 public CSNGenerator(int serverId, long timestamp) 058 { 059 this.lastTime = timestamp; 060 this.serverId = serverId; 061 this.seqnum = 0; 062 } 063 064 /** 065 * Create a new {@link CSNGenerator}. 066 * 067 * @param serverId serverId to use when creating {@link CSN}s. 068 * @param state This generator will be created in a way that makes sure that 069 * all {@link CSN}s generated will be larger than all the 070 * {@link CSN}s currently in state. 071 */ 072 public CSNGenerator(int serverId, ServerState state) 073 { 074 this.lastTime = TimeThread.getTime(); 075 for (CSN csn : state) 076 { 077 if (this.lastTime < csn.getTime()) 078 { 079 this.lastTime = csn.getTime(); 080 } 081 if (csn.getServerId() == serverId) 082 { 083 this.seqnum = csn.getSeqnum(); 084 } 085 } 086 this.serverId = serverId; 087 } 088 089 /** 090 * Generate a new {@link CSN}. 091 * 092 * @return the generated {@link CSN} 093 */ 094 public CSN newCSN() 095 { 096 long curTime = TimeThread.getTime(); 097 int mySeqnum; 098 long myTime; 099 100 synchronized(this) 101 { 102 if (curTime > lastTime) 103 { 104 lastTime = curTime; 105 } 106 107 if (++seqnum <= 0) // check no underflow happened 108 { 109 seqnum = 0; 110 lastTime++; 111 } 112 mySeqnum = seqnum; 113 myTime = lastTime; 114 } 115 116 return new CSN(myTime, mySeqnum, serverId); 117 } 118 119 /** 120 * Adjust the lastTime of this {@link CSNGenerator} with a {@link CSN} that we 121 * have received from another server. 122 * <p> 123 * This is necessary because we need that the {@link CSN} generated after 124 * processing an update received from other hosts to be larger than the 125 * received {@link CSN} 126 * 127 * @param number 128 * the {@link CSN} to adjust with 129 */ 130 public void adjust(CSN number) 131 { 132 if (number==null) 133 { 134 synchronized(this) 135 { 136 lastTime = TimeThread.getTime(); 137 seqnum = 0; 138 } 139 return; 140 } 141 142 long rcvdTime = number.getTime(); 143 144 int changeServerId = number.getServerId(); 145 int changeSeqNum = number.getSeqnum(); 146 147 /* 148 * need to synchronize with newCSN method so that we protect writing 149 * lastTime fields 150 */ 151 synchronized(this) 152 { 153 if (lastTime <= rcvdTime) 154 { 155 lastTime = ++rcvdTime; 156 } 157 158 if (serverId == changeServerId && seqnum < changeSeqNum) 159 { 160 seqnum = changeSeqNum; 161 } 162 } 163 } 164 165 /** 166 * Adjust utility method that takes ServerState as a parameter. 167 * @param state the ServerState to adjust with 168 */ 169 public void adjust(ServerState state) 170 { 171 for (CSN csn : state) 172 { 173 adjust(csn); 174 } 175 } 176}