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}