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 2014-2016 ForgeRock AS.
015 */
016package org.opends.server.replication.server.changelog.file;
017
018import java.util.Iterator;
019import java.util.Map.Entry;
020import java.util.concurrent.ConcurrentSkipListMap;
021
022import net.jcip.annotations.NotThreadSafe;
023
024import org.opends.server.replication.common.CSN;
025import org.opends.server.replication.protocol.UpdateMsg;
026import org.opends.server.replication.server.changelog.api.ChangelogException;
027import org.opends.server.replication.server.changelog.api.DBCursor;
028import org.opends.server.replication.server.changelog.api.ReplicationDomainDB;
029import org.forgerock.opendj.ldap.DN;
030
031/** Cursor iterating over a replication domain's replica DBs. */
032@NotThreadSafe
033public class DomainDBCursor extends CompositeDBCursor<Void>
034{
035  /** Replaces null CSNs in ConcurrentSkipListMap that does not support null values. */
036  private static final CSN NULL_CSN = new CSN(0, 0, 0);
037
038  private final DN baseDN;
039  private final ReplicationDomainDB domainDB;
040  private final ConcurrentSkipListMap<Integer, CSN> newReplicas = new ConcurrentSkipListMap<>();
041  private final CursorOptions options;
042
043  /**
044   * Builds a DomainDBCursor instance.
045   *
046   * @param baseDN
047   *          the replication domain baseDN of this cursor
048   * @param domainDB
049   *          the DB for the provided replication domain
050   * @param options The cursor options
051   */
052  public DomainDBCursor(final DN baseDN, final ReplicationDomainDB domainDB, CursorOptions options)
053  {
054    this.baseDN = baseDN;
055    this.domainDB = domainDB;
056    this.options = options;
057  }
058
059  /**
060   * Returns the replication domain baseDN of this cursor.
061   *
062   * @return the replication domain baseDN of this cursor.
063   */
064  public DN getBaseDN()
065  {
066    return baseDN;
067  }
068
069  /**
070   * Adds a replicaDB for this cursor to iterate over. Added cursors will be
071   * created and iterated over on the next call to {@link #next()}.
072   *
073   * @param serverId
074   *          the serverId of the replica
075   * @param startCSN
076   *          the CSN to use as a starting point
077   */
078  public void addReplicaDB(int serverId, CSN startCSN)
079  {
080    // only keep the oldest CSN that will be the new cursor's starting point
081    newReplicas.putIfAbsent(serverId, startCSN != null ? startCSN : NULL_CSN);
082  }
083
084  /** {@inheritDoc} */
085  @Override
086  protected void incorporateNewCursors() throws ChangelogException
087  {
088    for (Iterator<Entry<Integer, CSN>> iter = newReplicas.entrySet().iterator(); iter.hasNext();)
089    {
090      final Entry<Integer, CSN> pair = iter.next();
091      final int serverId = pair.getKey();
092      final CSN csn = pair.getValue();
093      final CSN startCSN = !NULL_CSN.equals(csn) ? csn : null;
094      final DBCursor<UpdateMsg> cursor = domainDB.getCursorFrom(baseDN, serverId, startCSN, options);
095      addCursor(cursor, null);
096      iter.remove();
097    }
098  }
099
100  /** {@inheritDoc} */
101  @Override
102  public void close()
103  {
104    super.close();
105    domainDB.unregisterCursor(this);
106    newReplicas.clear();
107  }
108
109}