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 2013-2016 ForgeRock AS.
015 */
016package org.opends.server.replication.server;
017
018import java.util.HashSet;
019import java.util.Map;
020import java.util.Set;
021import java.util.concurrent.ConcurrentSkipListMap;
022
023import net.jcip.annotations.ThreadSafe;
024
025import org.opends.server.replication.common.CSN;
026import org.opends.server.replication.common.MultiDomainServerState;
027import org.forgerock.opendj.ldap.DN;
028
029/**
030 * This is the changelog state stored in the changelogStateDB. For each
031 * replication domain, it contains:
032 * <ul>
033 * <li>its generationId</li>
034 * <li>the list of serverIds composing it</li>
035 * </ul>
036 * <p>
037 * This class is used during replication initialization to decouple the code
038 * that reads the changelogStateDB from the code that makes use of its data.
039 */
040@ThreadSafe
041public class ChangelogState
042{
043  private final ConcurrentSkipListMap<DN, Long> domainToGenerationId = new ConcurrentSkipListMap<>();
044  private final ConcurrentSkipListMap<DN, Set<Integer>> domainToServerIds = new ConcurrentSkipListMap<>();
045  private final MultiDomainServerState offlineReplicas = new MultiDomainServerState();
046
047  /**
048   * Sets the generationId for the supplied replication domain.
049   *
050   * @param baseDN
051   *          the targeted replication domain baseDN
052   * @param generationId
053   *          the generation Id to set
054   */
055  public void setDomainGenerationId(DN baseDN, long generationId)
056  {
057    domainToGenerationId.put(baseDN, generationId);
058  }
059
060  /**
061   * Adds the serverId to the serverIds list of the supplied replication domain.
062   *
063   * @param serverId
064   *          the serverId to add
065   * @param baseDN
066   *          the targeted replication domain baseDN
067   */
068  public void addServerIdToDomain(int serverId, DN baseDN)
069  {
070    Set<Integer> serverIds = domainToServerIds.get(baseDN);
071    if (serverIds == null)
072    {
073      serverIds = new HashSet<>();
074      final Set<Integer> existingServerIds =
075          domainToServerIds.putIfAbsent(baseDN, serverIds);
076      if (existingServerIds != null)
077      {
078        serverIds = existingServerIds;
079      }
080    }
081    serverIds.add(serverId);
082  }
083
084  /**
085   * Adds the following replica information to the offline list.
086   *
087   * @param baseDN
088   *          the baseDN of the offline replica
089   * @param offlineCSN
090   *          the CSN (serverId + timestamp) of the offline replica
091   */
092  public void addOfflineReplica(DN baseDN, CSN offlineCSN)
093  {
094    offlineReplicas.update(baseDN, offlineCSN);
095  }
096
097  /**
098   * Removes the following replica information from the offline list.
099   *
100   * @param baseDN
101   *          the baseDN of the offline replica
102   * @param serverId
103   *          the serverId that is not offline anymore
104   */
105  public void removeOfflineReplica(DN baseDN, int serverId)
106  {
107    CSN csn;
108    do
109    {
110      csn = offlineReplicas.getCSN(baseDN, serverId);
111    }
112    while (csn != null && !offlineReplicas.removeCSN(baseDN, csn));
113  }
114
115  /**
116   * Returns the Map of domainBaseDN => generationId.
117   *
118   * @return a Map of domainBaseDN => generationId
119   */
120  public Map<DN, Long> getDomainToGenerationId()
121  {
122    return domainToGenerationId;
123  }
124
125  /**
126   * Returns the Map of domainBaseDN => List&lt;serverId&gt;.
127   *
128   * @return a Map of domainBaseDN => List&lt;serverId&gt;.
129   */
130  public Map<DN, Set<Integer>> getDomainToServerIds()
131  {
132    return domainToServerIds;
133  }
134
135  /**
136   * Returns the internal MultiDomainServerState for offline replicas.
137   *
138   * @return the MultiDomainServerState for offline replicas.
139   */
140  public MultiDomainServerState getOfflineReplicas()
141  {
142    return offlineReplicas;
143  }
144
145  /**
146   * Returns whether the current ChangelogState is equal to the provided
147   * ChangelogState.
148   * <p>
149   * Note: Only use for tests!!<br>
150   * This method should only be used by tests because it creates a lot of
151   * intermediate objects which is not suitable for production.
152   *
153   * @param other
154   *          the ChangelogState to compare with
155   * @return true if the current ChangelogState is equal to the provided
156   *         ChangelogState, false otherwise.
157   */
158  public boolean isEqualTo(ChangelogState other)
159  {
160    if (other == null)
161    {
162      return false;
163    }
164    if (this == other)
165    {
166      return true;
167    }
168    return domainToGenerationId.equals(other.domainToGenerationId)
169        && domainToServerIds.equals(other.domainToServerIds)
170        // Note: next line is not suitable for production
171        // because it creates lots of Lists and Maps
172        && offlineReplicas.getSnapshot().equals(other.offlineReplicas.getSnapshot());
173  }
174
175  /** {@inheritDoc} */
176  @Override
177  public String toString()
178  {
179    return "domainToGenerationId=" + domainToGenerationId
180        + ", domainToServerIds=" + domainToServerIds
181        + ", offlineReplicas=" + offlineReplicas;
182  }
183}