/*
|
* The contents of this file are subject to the terms of the Common Development and
|
* Distribution License (the License). You may not use this file except in compliance with the
|
* License.
|
*
|
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
|
* specific language governing permission and limitations under the License.
|
*
|
* When distributing Covered Software, include this CDDL Header Notice in each file and include
|
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
|
* Header, with the fields enclosed by brackets [] replaced by your own identifying
|
* information: "Portions Copyright [year] [name of copyright owner]".
|
*
|
* Copyright 2006-2009 Sun Microsystems, Inc.
|
* Portions Copyright 2011-2014 ForgeRock AS.
|
*/
|
package org.opends.server.replication.common;
|
|
import org.opends.server.util.TimeThread;
|
|
/**
|
* This class defines a structure that is used for storing the last {@link CSN}s
|
* generated on this server or received from other servers and generating new
|
* {@link CSN}s that are guaranteed to be larger than all the previously seen or
|
* generated CSNs.
|
*/
|
public class CSNGenerator
|
{
|
private long lastTime;
|
/**
|
* The sequence number allows to distinguish changes that have been done in
|
* the same millisecond.
|
*
|
* @see #lastTime
|
*/
|
private int seqnum;
|
private final int serverId;
|
|
/**
|
* Create a new {@link CSNGenerator}.
|
*
|
* @param serverId
|
* id to use when creating {@link CSN}s.
|
* @param timestamp
|
* time to start with.
|
*/
|
public CSNGenerator(int serverId, long timestamp)
|
{
|
this.lastTime = timestamp;
|
this.serverId = serverId;
|
this.seqnum = 0;
|
}
|
|
/**
|
* Create a new {@link CSNGenerator}.
|
*
|
* @param serverId serverId to use when creating {@link CSN}s.
|
* @param state This generator will be created in a way that makes sure that
|
* all {@link CSN}s generated will be larger than all the
|
* {@link CSN}s currently in state.
|
*/
|
public CSNGenerator(int serverId, ServerState state)
|
{
|
this.lastTime = TimeThread.getTime();
|
for (CSN csn : state)
|
{
|
if (this.lastTime < csn.getTime())
|
{
|
this.lastTime = csn.getTime();
|
}
|
if (csn.getServerId() == serverId)
|
{
|
this.seqnum = csn.getSeqnum();
|
}
|
}
|
this.serverId = serverId;
|
}
|
|
/**
|
* Generate a new {@link CSN}.
|
*
|
* @return the generated {@link CSN}
|
*/
|
public CSN newCSN()
|
{
|
long curTime = TimeThread.getTime();
|
int mySeqnum;
|
long myTime;
|
|
synchronized(this)
|
{
|
if (curTime > lastTime)
|
{
|
lastTime = curTime;
|
}
|
|
if (++seqnum <= 0) // check no underflow happened
|
{
|
seqnum = 0;
|
lastTime++;
|
}
|
mySeqnum = seqnum;
|
myTime = lastTime;
|
}
|
|
return new CSN(myTime, mySeqnum, serverId);
|
}
|
|
/**
|
* Adjust the lastTime of this {@link CSNGenerator} with a {@link CSN} that we
|
* have received from another server.
|
* <p>
|
* This is necessary because we need that the {@link CSN} generated after
|
* processing an update received from other hosts to be larger than the
|
* received {@link CSN}
|
*
|
* @param number
|
* the {@link CSN} to adjust with
|
*/
|
public void adjust(CSN number)
|
{
|
if (number==null)
|
{
|
synchronized(this)
|
{
|
lastTime = TimeThread.getTime();
|
seqnum = 0;
|
}
|
return;
|
}
|
|
long rcvdTime = number.getTime();
|
|
int changeServerId = number.getServerId();
|
int changeSeqNum = number.getSeqnum();
|
|
/*
|
* need to synchronize with newCSN method so that we protect writing
|
* lastTime fields
|
*/
|
synchronized(this)
|
{
|
if (lastTime <= rcvdTime)
|
{
|
lastTime = ++rcvdTime;
|
}
|
|
if (serverId == changeServerId && seqnum < changeSeqNum)
|
{
|
seqnum = changeSeqNum;
|
}
|
}
|
}
|
|
/**
|
* Adjust utility method that takes ServerState as a parameter.
|
* @param state the ServerState to adjust with
|
*/
|
public void adjust(ServerState state)
|
{
|
for (CSN csn : state)
|
{
|
adjust(csn);
|
}
|
}
|
}
|